OSDN Git Service

Initial revision
[pf3gnuchains/sourceware.git] / expect / exp_strf.c
1 /* exp_strp.c - functions for exp_timestamp */
2 /*
3  * strftime.c
4  *
5  * Public-domain implementation of ANSI C library routine.
6  *
7  * It's written in old-style C for maximal portability.
8  * However, since I'm used to prototypes, I've included them too.
9  *
10  * If you want stuff in the System V ascftime routine, add the SYSV_EXT define.
11  * For extensions from SunOS, add SUNOS_EXT.
12  * For stuff needed to implement the P1003.2 date command, add POSIX2_DATE.
13  * For VMS dates, add VMS_EXT.
14  * For complete POSIX semantics, add POSIX_SEMANTICS.
15  *
16  * The code for %c, %x, and %X now follows the 1003.2 specification for
17  * the POSIX locale.
18  * This version ignores LOCALE information.
19  * It also doesn't worry about multi-byte characters.
20  * So there.
21  *
22  * This file is also shipped with GAWK (GNU Awk), gawk specific bits of
23  * code are included if GAWK is defined.
24  *
25  * Arnold Robbins <arnold@skeeve.atl.ga.us>
26  * January, February, March, 1991
27  * Updated March, April 1992
28  * Updated April, 1993
29  * Updated February, 1994
30  * Updated May, 1994
31  * Updated January 1995
32  * Updated September 1995
33  *
34  * Fixes from ado@elsie.nci.nih.gov
35  * February 1991, May 1992
36  * Fixes from Tor Lillqvist tml@tik.vtt.fi
37  * May, 1993
38  * Further fixes from ado@elsie.nci.nih.gov
39  * February 1994
40  * %z code from chip@chinacat.unicom.com
41  * Applied September 1995
42  *
43  *
44  * Modified by Don Libes for Expect, 10/93 and 12/95.
45  * Forced POSIX semantics.
46  * Replaced inline/min/max stuff with a single range function.
47  * Removed tzset stuff.
48  * Commented out tzname stuff.
49  *
50  * According to Arnold, the current version of this code can ftp'd from
51  * ftp.mathcs.emory.edu:/pub/arnold/strftime.shar.gz
52  *
53  */
54
55 #include "expect_cf.h"
56 #include "tcl.h"
57
58 #include <stdio.h>
59 #include <ctype.h>
60 #include "string.h"
61
62 /* according to Karl Vogel, time.h is insufficient on Pyramid */
63 /* following is recommended by autoconf */
64
65 #ifdef TIME_WITH_SYS_TIME
66 # include <sys/time.h>
67 # include <time.h>
68 #else
69 # ifdef HAVE_SYS_TIME_H
70 #  include <sys/time.h>
71 # else
72 #  include <time.h>
73 # endif
74 #endif
75
76
77
78 #include <sys/types.h>
79
80 #define SYSV_EXT        1       /* stuff in System V ascftime routine */
81 #define POSIX2_DATE     1       /* stuff in Posix 1003.2 date command */
82
83 #if defined(POSIX2_DATE) && ! defined(SYSV_EXT)
84 #define SYSV_EXT        1
85 #endif
86
87 #if defined(POSIX2_DATE)
88 #define adddecl(stuff)  stuff
89 #else
90 #define adddecl(stuff)
91 #endif
92
93 #ifndef __STDC__
94 #define const
95
96 extern char *getenv();
97 static int weeknumber();
98 adddecl(static int iso8601wknum();)
99 #else
100
101 #ifndef strchr
102 extern char *strchr(const char *str, int ch);
103 #endif
104 extern char *getenv(const char *v);
105 static int weeknumber(const struct tm *timeptr, int firstweekday);
106 adddecl(static int iso8601wknum(const struct tm *timeptr);)
107 #endif
108
109 /* attempt to use strftime to compute timezone, else fallback to */
110 /* less portable ways */
111 #if !defined(HAVE_STRFTIME)
112 # if defined(HAVE_SV_TIMEZONE)
113 extern char *tzname[2];
114 extern int daylight;
115 # else
116 #  if defined(HAVE_TIMEZONE)
117
118 char           *
119 zone_name (tp)
120 struct tm      *tp;
121 {
122         char           *timezone ();
123         struct timeval  tv;
124         struct timezone tz;
125
126         gettimeofday (&tv, &tz);
127
128         return timezone (tz.tz_minuteswest, tp->tm_isdst);
129 }
130
131 #  endif /* HAVE_TIMEZONE */
132 # endif /* HAVE_SV_TIMEZONE */
133 #endif /* HAVE_STRFTIME */
134
135 static int
136 range(low,item,hi)
137 int low, item, hi;
138 {
139         if (item < low) return low;
140         if (item > hi) return hi;
141         return item;
142 }
143
144 /* strftime --- produce formatted time */
145
146 void
147 /*size_t*/
148 #ifndef __STDC__
149 exp_strftime(/*s,*/ format, timeptr, dstring)
150 /*char *s;*/
151 char *format;
152 const struct tm *timeptr;
153 Tcl_DString *dstring;
154 #else
155 /*exp_strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)*/
156 exp_strftime(char *format, const struct tm *timeptr,Tcl_DString *dstring)
157 #endif
158 {
159         int copied;     /* used to suppress copying when called recursively */
160
161 #if 0
162         char *endp = s + maxsize;
163         char *start = s;
164 #endif
165         char *percentptr;
166
167         char tbuf[100];
168         int i;
169
170         /* various tables, useful in North America */
171         static char *days_a[] = {
172                 "Sun", "Mon", "Tue", "Wed",
173                 "Thu", "Fri", "Sat",
174         };
175         static char *days_l[] = {
176                 "Sunday", "Monday", "Tuesday", "Wednesday",
177                 "Thursday", "Friday", "Saturday",
178         };
179         static char *months_a[] = {
180                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
181                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
182         };
183         static char *months_l[] = {
184                 "January", "February", "March", "April",
185                 "May", "June", "July", "August", "September",
186                 "October", "November", "December",
187         };
188         static char *ampm[] = { "AM", "PM", };
189
190 /*      for (; *format && s < endp - 1; format++) {*/
191         for (; *format ; format++) {
192                 tbuf[0] = '\0';
193                 copied = 0;             /* has not been copied yet */
194                 percentptr = strchr(format,'%');
195                 if (percentptr == 0) {
196                         Tcl_DStringAppend(dstring,format,-1);
197                         goto out;
198                 } else if (percentptr != format) {
199                         Tcl_DStringAppend(dstring,format,percentptr - format);
200                         format = percentptr;
201                 }
202 #if 0
203                 if (*format != '%') {
204                         *s++ = *format;
205                         continue;
206                 }
207 #endif
208         again:
209                 switch (*++format) {
210                 case '\0':
211                         Tcl_DStringAppend(dstring,"%",1);
212 #if 0
213                         *s++ = '%';
214 #endif
215                         goto out;
216
217                 case '%':
218                         Tcl_DStringAppend(dstring,"%",1);
219                         copied = 1;
220                         break;
221 #if 0
222                         *s++ = '%';
223                         continue;
224 #endif
225
226                 case 'a':       /* abbreviated weekday name */
227                         if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
228                                 strcpy(tbuf, "?");
229                         else
230                                 strcpy(tbuf, days_a[timeptr->tm_wday]);
231                         break;
232
233                 case 'A':       /* full weekday name */
234                         if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
235                                 strcpy(tbuf, "?");
236                         else
237                                 strcpy(tbuf, days_l[timeptr->tm_wday]);
238                         break;
239
240 #ifdef SYSV_EXT
241                 case 'h':       /* abbreviated month name */
242 #endif
243                 case 'b':       /* abbreviated month name */
244                         if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
245                                 strcpy(tbuf, "?");
246                         else
247                                 strcpy(tbuf, months_a[timeptr->tm_mon]);
248                         break;
249
250                 case 'B':       /* full month name */
251                         if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
252                                 strcpy(tbuf, "?");
253                         else
254                                 strcpy(tbuf, months_l[timeptr->tm_mon]);
255                         break;
256
257                 case 'c':       /* appropriate date and time representation */
258                         sprintf(tbuf, "%s %s %2d %02d:%02d:%02d %d",
259                                 days_a[range(0, timeptr->tm_wday, 6)],
260                                 months_a[range(0, timeptr->tm_mon, 11)],
261                                 range(1, timeptr->tm_mday, 31),
262                                 range(0, timeptr->tm_hour, 23),
263                                 range(0, timeptr->tm_min, 59),
264                                 range(0, timeptr->tm_sec, 61),
265                                 timeptr->tm_year + 1900);
266                         break;
267
268                 case 'd':       /* day of the month, 01 - 31 */
269                         i = range(1, timeptr->tm_mday, 31);
270                         sprintf(tbuf, "%02d", i);
271                         break;
272
273                 case 'H':       /* hour, 24-hour clock, 00 - 23 */
274                         i = range(0, timeptr->tm_hour, 23);
275                         sprintf(tbuf, "%02d", i);
276                         break;
277
278                 case 'I':       /* hour, 12-hour clock, 01 - 12 */
279                         i = range(0, timeptr->tm_hour, 23);
280                         if (i == 0)
281                                 i = 12;
282                         else if (i > 12)
283                                 i -= 12;
284                         sprintf(tbuf, "%02d", i);
285                         break;
286
287                 case 'j':       /* day of the year, 001 - 366 */
288                         sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
289                         break;
290
291                 case 'm':       /* month, 01 - 12 */
292                         i = range(0, timeptr->tm_mon, 11);
293                         sprintf(tbuf, "%02d", i + 1);
294                         break;
295
296                 case 'M':       /* minute, 00 - 59 */
297                         i = range(0, timeptr->tm_min, 59);
298                         sprintf(tbuf, "%02d", i);
299                         break;
300
301                 case 'p':       /* am or pm based on 12-hour clock */
302                         i = range(0, timeptr->tm_hour, 23);
303                         if (i < 12)
304                                 strcpy(tbuf, ampm[0]);
305                         else
306                                 strcpy(tbuf, ampm[1]);
307                         break;
308
309                 case 'S':       /* second, 00 - 61 */
310                         i = range(0, timeptr->tm_sec, 61);
311                         sprintf(tbuf, "%02d", i);
312                         break;
313
314                 case 'U':       /* week of year, Sunday is first day of week */
315                         sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
316                         break;
317
318                 case 'w':       /* weekday, Sunday == 0, 0 - 6 */
319                         i = range(0, timeptr->tm_wday, 6);
320                         sprintf(tbuf, "%d", i);
321                         break;
322
323                 case 'W':       /* week of year, Monday is first day of week */
324                         sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
325                         break;
326
327                 case 'x':       /* appropriate date representation */
328                         sprintf(tbuf, "%s %s %2d %d",
329                                 days_a[range(0, timeptr->tm_wday, 6)],
330                                 months_a[range(0, timeptr->tm_mon, 11)],
331                                 range(1, timeptr->tm_mday, 31),
332                                 timeptr->tm_year + 1900);
333                         break;
334
335                 case 'X':       /* appropriate time representation */
336                         sprintf(tbuf, "%02d:%02d:%02d",
337                                 range(0, timeptr->tm_hour, 23),
338                                 range(0, timeptr->tm_min, 59),
339                                 range(0, timeptr->tm_sec, 61));
340                         break;
341
342                 case 'y':       /* year without a century, 00 - 99 */
343                         i = timeptr->tm_year % 100;
344                         sprintf(tbuf, "%02d", i);
345                         break;
346
347                 case 'Y':       /* year with century */
348                         sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
349                         break;
350
351                 case 'Z':       /* time zone name or abbrevation */
352 #if defined(HAVE_STRFTIME)
353                         strftime(tbuf,sizeof tbuf,"%Z",timeptr);
354 #else
355 # if defined(HAVE_SV_TIMEZONE)
356                         i = 0;
357                         if (daylight && timeptr->tm_isdst)
358                                 i = 1;
359                         strcpy(tbuf, tzname[i]);
360 # else
361                         strcpy(tbuf, zone_name (timeptr));
362 #  if defined(HAVE_TIMEZONE)
363 #  endif /* HAVE_TIMEZONE */
364                         /* no timezone available */
365                         /* feel free to add others here */
366 # endif /* HAVE_SV_TIMEZONE */
367 #endif /* HAVE STRFTIME */
368                         break;
369
370 #ifdef SYSV_EXT
371                 case 'n':       /* same as \n */
372                         tbuf[0] = '\n';
373                         tbuf[1] = '\0';
374                         break;
375
376                 case 't':       /* same as \t */
377                         tbuf[0] = '\t';
378                         tbuf[1] = '\0';
379                         break;
380
381                 case 'D':       /* date as %m/%d/%y */
382                         exp_strftime("%m/%d/%y", timeptr, dstring);
383                         copied = 1;
384 /*                      exp_strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);*/
385                         break;
386
387                 case 'e':       /* day of month, blank padded */
388                         sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
389                         break;
390
391                 case 'r':       /* time as %I:%M:%S %p */
392                         exp_strftime("%I:%M:%S %p", timeptr, dstring);
393                         copied = 1;
394 /*                      exp_strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);*/
395                         break;
396
397                 case 'R':       /* time as %H:%M */
398                         exp_strftime("%H:%M", timeptr, dstring);
399                         copied = 1;
400 /*                      exp_strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);*/
401                         break;
402
403                 case 'T':       /* time as %H:%M:%S */
404                         exp_strftime("%H:%M:%S", timeptr, dstring);
405                         copied = 1;
406 /*                      exp_strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);*/
407                         break;
408 #endif
409
410 #ifdef POSIX2_DATE
411                 case 'C':
412                         sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100);
413                         break;
414
415
416                 case 'E':
417                 case 'O':
418                         /* POSIX locale extensions, ignored for now */
419                         goto again;
420                 case 'V':       /* week of year according ISO 8601 */
421                         sprintf(tbuf, "%02d", iso8601wknum(timeptr));
422                         break;
423
424                 case 'u':
425                 /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
426                         sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
427                                         timeptr->tm_wday);
428                         break;
429 #endif  /* POSIX2_DATE */
430                 default:
431                         tbuf[0] = '%';
432                         tbuf[1] = *format;
433                         tbuf[2] = '\0';
434                         break;
435                 }
436                 if (!copied)
437                         Tcl_DStringAppend(dstring,tbuf,-1);
438 #if 0
439                 i = strlen(tbuf);
440                 if (i) {
441                         if (s + i < endp - 1) {
442                                 strcpy(s, tbuf);
443                                 s += i;
444                         } else
445                                 return 0;
446 #endif
447         }
448 out:;
449 #if 0
450         if (s < endp && *format == '\0') {
451                 *s = '\0';
452                 return (s - start);
453         } else
454                 return 0;
455 #endif
456 }
457
458 /* isleap --- is a year a leap year? */
459
460 #ifndef __STDC__
461 static int
462 isleap(year)
463 int year;
464 #else
465 static int
466 isleap(int year)
467 #endif
468 {
469         return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
470 }
471
472 #ifdef POSIX2_DATE
473 /* iso8601wknum --- compute week number according to ISO 8601 */
474
475 #ifndef __STDC__
476 static int
477 iso8601wknum(timeptr)
478 const struct tm *timeptr;
479 #else
480 static int
481 iso8601wknum(const struct tm *timeptr)
482 #endif
483 {
484         /*
485          * From 1003.2:
486          *      If the week (Monday to Sunday) containing January 1
487          *      has four or more days in the new year, then it is week 1;
488          *      otherwise it is the highest numbered week of the previous
489          *      (52 or 53) year, and the next week is week 1.
490          *
491          * ADR: This means if Jan 1 was Monday through Thursday,
492          *      it was week 1, otherwise week 53.
493          * 
494          * XPG4 erroneously included POSIX.2 rationale text in the
495          * main body of the standard. Thus it requires week 53.
496          */
497
498         int weeknum, jan1day, diff;
499
500         /* get week number, Monday as first day of the week */
501         weeknum = weeknumber(timeptr, 1);
502
503         /*
504          * With thanks and tip of the hatlo to tml@tik.vtt.fi
505          *
506          * What day of the week does January 1 fall on?
507          * We know that
508          *      (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
509          *              (timeptr->tm_wday - jan1.tm_wday) MOD 7
510          * and that
511          *      jan1.tm_yday == 0
512          * and that
513          *      timeptr->tm_wday MOD 7 == timeptr->tm_wday
514          * from which it follows that. . .
515          */
516         jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
517         if (jan1day < 0)
518                 jan1day += 7;
519
520         /*
521          * If Jan 1 was a Monday through Thursday, it was in
522          * week 1.  Otherwise it was last year's highest week, which is
523          * this year's week 0.
524          *
525          * What does that mean?
526          * If Jan 1 was Monday, the week number is exactly right, it can
527          *      never be 0.
528          * If it was Tuesday through Thursday, the weeknumber is one
529          *      less than it should be, so we add one.
530          * Otherwise, Friday, Saturday or Sunday, the week number is
531          * OK, but if it is 0, it needs to be 52 or 53.
532          */
533         switch (jan1day) {
534         case 1:         /* Monday */
535                 break;
536         case 2:         /* Tuesday */
537         case 3:         /* Wednesday */
538         case 4:         /* Thursday */
539                 weeknum++;
540                 break;
541         case 5:         /* Friday */
542         case 6:         /* Saturday */
543         case 0:         /* Sunday */
544                 if (weeknum == 0) {
545 #ifdef USE_BROKEN_XPG4
546                         /* XPG4 (as of March 1994) says 53 unconditionally */
547                         weeknum = 53;
548 #else
549                         /* get week number of last week of last year */
550                         struct tm dec31ly;      /* 12/31 last year */
551                         dec31ly = *timeptr;
552                         dec31ly.tm_year--;
553                         dec31ly.tm_mon = 11;
554                         dec31ly.tm_mday = 31;
555                         dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
556                         dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900);
557                         weeknum = iso8601wknum(& dec31ly);
558 #endif
559                 }
560                 break;
561         }
562
563         if (timeptr->tm_mon == 11) {
564                 /*
565                  * The last week of the year
566                  * can be in week 1 of next year.
567                  * Sigh.
568                  *
569                  * This can only happen if
570                  *      M   T  W
571                  *      29  30 31
572                  *      30  31
573                  *      31
574                  */
575                 int wday, mday;
576
577                 wday = timeptr->tm_wday;
578                 mday = timeptr->tm_mday;
579                 if (   (wday == 1 && (mday >= 29 && mday <= 31))
580                     || (wday == 2 && (mday == 30 || mday == 31))
581                     || (wday == 3 &&  mday == 31))
582                         weeknum = 1;
583         }
584
585         return weeknum;
586 }
587 #endif
588
589 /* weeknumber --- figure how many weeks into the year */
590
591 /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
592
593 #ifndef __STDC__
594 static int
595 weeknumber(timeptr, firstweekday)
596 const struct tm *timeptr;
597 int firstweekday;
598 #else
599 static int
600 weeknumber(const struct tm *timeptr, int firstweekday)
601 #endif
602 {
603         int wday = timeptr->tm_wday;
604         int ret;
605
606         if (firstweekday == 1) {
607                 if (wday == 0)  /* sunday */
608                         wday = 6;
609                 else
610                         wday--;
611         }
612         ret = ((timeptr->tm_yday + 7 - wday) / 7);
613         if (ret < 0)
614                 ret = 0;
615         return ret;
616 }