1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 1996 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984 AT&T */
28 /* All Rights Reserved */
29
30
31 #pragma ident "%Z%%M% %I% %E% SMI"
32
33 #if !defined(lint) && defined(SCCSIDS)
34 static char *sccsid = "%Z%%M% %I% %E% SMI"; /* from S5R3.1 cftime.c 1.9 */
35 #endif
36
37 /*LINTLIBRARY*/
38
39 #include <locale.h>
40 #include <time.h>
41 #include <string.h>
42 #include <sys/param.h>
43 #include <sys/stat.h>
44
45 static char *getstr(/*char *p, char **strp*/);
46 static char *itoa();
47 extern int stat();
48 extern char *getenv();
49 extern char *malloc();
50 extern int openlocale(/*char *category, int cat_id, char *locale, char *newlocale */);
51 extern void init_statics();
52
53 extern struct dtconv *_dtconv;
54 extern char _locales[MAXLOCALE + 1][MAXLOCALENAME + 1];
55 extern char _my_time[];
56
57 char *dtconv_str = NULL;
58 char *getlocale_time();
59
60 int
strftime(buf,maxsize,format,tm)61 strftime(buf, maxsize, format, tm)
62 char *buf, *format;
63 struct tm *tm;
64 {
65 register char *cp, *p, c;
66 int size;
67 int i, temp;
68 register struct dtconv *dtcp;
69
70 (void) getlocale_time();
71 dtcp = localdtconv(); /* get locale's strings */
72
73 /* Build date string by parsing format string */
74 cp = buf;
75 size = 0;
76 while ((c = *format++) != '\0') {
77 if (c == '%') {
78 switch (*format++) {
79
80 case '%': /* Percent sign */
81 if (++size >= maxsize)
82 return (0);
83 *cp++ = '%';
84 break;
85
86 case 'a': /* Abbreviated weekday name */
87 for (p = dtcp->abbrev_weekday_names[tm->tm_wday];
88 *p != '\0'; p++) {
89 if (++size >= maxsize)
90 return (0);
91 *cp++ = *p;
92 }
93 break;
94
95 case 'A': /* Weekday name */
96 for (p = dtcp->weekday_names[tm->tm_wday];
97 *p != '\0'; p++) {
98 if (++size >= maxsize)
99 return (0);
100 *cp++ = *p;
101 }
102 break;
103
104 case 'h':
105 case 'b': /* Abbreviated month name */
106 for (p = dtcp->abbrev_month_names[tm->tm_mon];
107 *p != '\0'; p++) {
108 if (++size >= maxsize)
109 return (0);
110 *cp++ = *p;
111 }
112 break;
113
114 case 'B': /* Month name */
115 for (p = dtcp->month_names[tm->tm_mon];
116 *p != '\0'; p++) {
117 if (++size >= maxsize)
118 return (0);
119 *cp++ = *p;
120 }
121 break;
122
123 case 'c': /* date and time representation */
124 i = strftime(cp, maxsize - size, "%x %X", tm);
125 if (i == 0)
126 return (0);
127 cp += i;
128 size += i;
129 break;
130
131 case 'C': /* long date and time representation */
132 i = strftime(cp, maxsize - size,
133 dtcp->ldate_format, tm);
134 if (i == 0)
135 return (0);
136 cp += i;
137 size += i;
138 break;
139
140 case 'd': /* Day of month, with leading zero */
141 if ((size += 2) >= maxsize)
142 return (0);
143 cp = itoa(tm->tm_mday, cp, 2);
144 break;
145
146 case 'D': /* Shorthand for %m/%d/%y */
147 i = strftime(cp, maxsize - size, "%m/%d/%y",
148 tm);
149 if (i == 0)
150 return (0);
151 cp += i;
152 size += i;
153 break;
154
155 case 'e': /* Day of month without leading zero */
156 if ((size += 2) >= maxsize)
157 return (0);
158 if (tm->tm_mday < 10) {
159 *cp++ = ' ';
160 cp = itoa(tm->tm_mday, cp, 1);
161 } else
162 cp = itoa(tm->tm_mday, cp, 2);
163 break;
164
165 case 'H': /* Hour (24 hour version) */
166 if ((size += 2) >= maxsize)
167 return (0);
168 cp = itoa(tm->tm_hour, cp, 2);
169 break;
170
171 case 'I': /* Hour (12 hour version) */
172 if ((size += 2) >= maxsize)
173 return (0);
174 cp = itoa(tm->tm_hour > 12 ?
175 tm->tm_hour - 12 :
176 (tm->tm_hour == 0 ? 12 : tm->tm_hour),
177 cp, 2);
178 break;
179
180 case 'j': /* Julian date */
181 if ((size += 3) >= maxsize)
182 return (0);
183 cp = itoa(tm->tm_yday + 1, cp, 3);
184 break;
185
186 case 'k': /* Hour (24 hour version) */
187 if ((size += 2) >= maxsize)
188 return (0);
189 if (tm->tm_hour < 10) {
190 *cp++ = ' ';
191 cp = itoa(tm->tm_hour, cp, 1);
192 } else
193 cp = itoa(tm->tm_hour, cp, 2);
194 break;
195
196 case 'l': /* Hour (12 hour version) */
197 if ((size += 2) >= maxsize)
198 return (0);
199 temp = tm->tm_hour > 12 ?
200 tm->tm_hour - 12 :
201 (tm->tm_hour == 0 ? 12 : tm->tm_hour);
202 if (temp < 10) {
203 *cp++ = ' ';
204 cp = itoa(temp, cp, 1);
205 } else
206 cp = itoa(temp, cp, 2);
207 break;
208
209 case 'm': /* Month number */
210 if ((size += 2) >= maxsize)
211 return (0);
212 cp = itoa(tm->tm_mon + 1, cp, 2);
213 break;
214
215 case 'M': /* Minute */
216 if ((size += 2) >= maxsize)
217 return (0);
218 cp = itoa(tm->tm_min, cp, 2);
219 break;
220
221 case 'n': /* Newline */
222 if (++size >= maxsize)
223 return (0);
224 *cp++ = '\n';
225 break;
226
227 case 'p': /* AM or PM */
228 if (tm->tm_hour >= 12)
229 p = dtcp->pm_string;
230 else
231 p = dtcp->am_string;
232 for (; *p != '\0'; p++) {
233 if (++size >= maxsize)
234 return (0);
235 *cp++ = *p;
236 }
237 break;
238
239 case 'r': /* Shorthand for %I:%M:%S AM or PM */
240 i = strftime(cp, maxsize - size, "%I:%M:%S %p",
241 tm);
242 if (i == 0)
243 return (0);
244 cp += i;
245 size += i;
246 break;
247
248 case 'R': /* Time as %H:%M */
249 i = strftime(cp, maxsize - size, "%H:%M", tm);
250 if (i == 0)
251 return (0);
252 cp += i;
253 size += i;
254 break;
255
256 case 'S': /* Seconds */
257 if ((size += 2) >= maxsize)
258 return (0);
259 cp = itoa(tm->tm_sec, cp, 2);
260 break;
261
262 case 't': /* Tab */
263 if (++size >= maxsize)
264 return (0);
265 *cp++ = '\t';
266 break;
267
268 case 'T': /* Shorthand for %H:%M:%S */
269 i = strftime(cp, maxsize - size, "%H:%M:%S",
270 tm);
271 if (i == 0)
272 return (0);
273 cp += i;
274 size += i;
275 break;
276
277 case 'U': /* Weekday number, taking Sunday as
278 * the first day of the week */
279 if ((size += 2) >= maxsize)
280 return (0);
281 temp = tm->tm_yday - tm->tm_wday;
282 if (temp >= -3 ) {
283 i = (temp + 1) / 7 + 1; /* +1 for - tm->tm_wday */
284 if (temp % 7 >= 4)
285 i++;
286 } else
287 i = 52;
288 cp = itoa(i, cp, 2);
289 break;
290
291 case 'w': /* Weekday number */
292 if (++size >= maxsize)
293 return (0);
294 cp = itoa(tm->tm_wday, cp, 1);
295 break;
296
297 case 'W': /* Week number of year, taking Monday as
298 * first day of week */
299 if ((size += 2) >= maxsize)
300 return (0);
301 if (tm->tm_wday == 0)
302 temp = tm->tm_yday - 6;
303 else
304 temp = tm->tm_yday - tm->tm_wday + 1;
305 if (temp >= -3) {
306 i = (temp + 1) / 7 + 1; /* 1 for
307 -tm->tm_wday */
308 if (temp % 7 >= 4)
309 i++;
310 } else
311 i = 52; /* less than 4 days in the first
312 week causes it to belong to
313 the tail of prev year */
314 cp = itoa(i, cp, 2);
315 break;
316
317 case 'x': /* Localized date format */
318 i = strftime(cp, maxsize - size,
319 dtcp->sdate_format, tm);
320 if (i == 0)
321 return (0);
322 cp += i;
323 size += i;
324 break;
325
326 case 'X': /* Localized time format */
327 i = strftime(cp, maxsize - size,
328 dtcp->time_format, tm);
329 if (i == 0)
330 return (0);
331 cp += i;
332 size += i;
333 break;
334
335 case 'y': /* Year in the form yy */
336 if ((size += 2) >= maxsize)
337 return (0);
338 cp = itoa((tm->tm_year% 100), cp, 2);
339 break;
340
341 case 'Y': /* Year in the form ccyy */
342 if ((size += 4) >= maxsize)
343 return (0);
344 cp = itoa(1900 + tm->tm_year, cp, 4);
345 break;
346
347 case 'Z': /* Timezone */
348 for(p = tm->tm_zone; *p != '\0'; p++) {
349 if (++size >= maxsize)
350 return (0);
351 *cp++ = *p;
352 }
353 break;
354
355 default:
356 if ((size += 2) >= maxsize)
357 return (0);
358 *cp++ = c;
359 *cp++ = *(format - 1);
360 break;
361 }
362 } else {
363 if (++size >= maxsize)
364 return (0);
365 *cp++ = c;
366 }
367 }
368 *cp = '\0';
369 return(size);
370 }
371
372 static char *
itoa(i,ptr,dig)373 itoa(i, ptr, dig)
374 register int i;
375 register char *ptr;
376 register int dig;
377 {
378 switch(dig) {
379 case 4:
380 *ptr++ = i / 1000 + '0';
381 i = i - i / 1000 * 1000;
382 case 3:
383 *ptr++ = i / 100 + '0';
384 i = i - i / 100 * 100;
385 case 2:
386 *ptr++ = i / 10 + '0';
387 case 1:
388 *ptr++ = i % 10 + '0';
389 }
390
391 return(ptr);
392 }
393
394 char *
getlocale_time()395 getlocale_time()
396 {
397 register int fd;
398 struct stat buf;
399 char *str;
400 register char *p;
401 register int i;
402 struct dtconv dtconvp;
403 char temp[MAXLOCALENAME + 1];
404
405 if (_locales[0][0] == '\0')
406 init_statics();
407
408 /* Here we use the string newlocales to set time constants
409 * which should have been saved
410 * from a previous call to setlocale. We deferred the read until now
411 */
412
413 if (strcmp(_my_time, _locales[LC_TIME -1]) == 0) {
414 if (dtconv_str == NULL) {
415 /*
416 * Below is executed if getlocale_time()
417 * is called when LC_TIME locale is initial
418 * C locale.
419 */
420 strcpy(temp, "C");
421 /*
422 * Just to make openlocale() to read LC_TIME file.
423 */
424 strcat(_locales[LC_TIME-1], temp);
425 goto initial;
426 }
427 return dtconv_str;
428 }
429 strcpy(temp, _locales[LC_TIME - 1]);
430 strcpy(_locales[LC_TIME - 1], _my_time);
431 initial:
432 if ((fd = openlocale("LC_TIME", LC_TIME, temp, _locales[LC_TIME - 1])) < 0)
433 return (NULL);
434 strcpy(_my_time, _locales[LC_TIME - 1]);
435 if (fd == 0)
436 return dtconv_str;
437 if ((fstat(fd, &buf)) != 0)
438 return (NULL);
439 if ((str = malloc((unsigned)buf.st_size + 2)) == NULL) {
440 close(fd);
441 return (NULL);
442 }
443
444 if ((read(fd, str, (int)buf.st_size)) != buf.st_size) {
445 close(fd);
446 free(str);
447 return (NULL);
448 }
449
450 /* Set last character of str to '\0' */
451 p = &str[buf.st_size];
452 *p++ = '\n';
453 *p = '\0';
454
455 /* p will "walk thru" str */
456 p = str;
457
458 for (i = 0; i < 12; i++) {
459 p = getstr(p, &dtconvp.abbrev_month_names[i]);
460 if (p == NULL)
461 goto fail;
462 }
463 for (i = 0; i < 12; i++) {
464 p = getstr(p, &dtconvp.month_names[i]);
465 if (p == NULL)
466 goto fail;
467 }
468 for (i = 0; i < 7; i++) {
469 p = getstr(p, &dtconvp.abbrev_weekday_names[i]);
470 if (p == NULL)
471 goto fail;
472 }
473 for (i = 0; i < 7; i++) {
474 p = getstr(p, &dtconvp.weekday_names[i]);
475 if (p == NULL)
476 goto fail;
477 }
478 p = getstr(p, &dtconvp.time_format);
479 if (p == NULL)
480 goto fail;
481 p = getstr(p, &dtconvp.sdate_format);
482 if (p == NULL)
483 goto fail;
484 p = getstr(p, &dtconvp.dtime_format);
485 if (p == NULL)
486 goto fail;
487 p = getstr(p, &dtconvp.am_string);
488 if (p == NULL)
489 goto fail;
490 p = getstr(p, &dtconvp.pm_string);
491 if (p == NULL)
492 goto fail;
493 p = getstr(p, &dtconvp.ldate_format);
494 if (p == NULL)
495 goto fail;
496 (void) close(fd);
497
498 /*
499 * set info.
500 */
501 if (dtconv_str != NULL)
502 free(dtconv_str);
503
504 dtconv_str = str;
505
506 /* The following is to get space malloc'd for _dtconv */
507
508 if (_dtconv == 0)
509 (void) localdtconv();
510 memcpy(_dtconv, &dtconvp, sizeof(struct dtconv));
511 return (dtconv_str);
512
513 fail:
514 (void) close(fd);
515 free(str);
516 return (NULL);
517 }
518
519
520 static char *
getstr(p,strp)521 getstr(p, strp)
522 register char *p;
523 char **strp;
524 {
525 *strp = p;
526 p = strchr(p, '\n');
527 if (p == NULL)
528 return (NULL); /* no end-of-line */
529 *p++ = '\0';
530 return (p);
531 }
532