xref: /titanic_50/usr/src/lib/libbc/libc/gen/common/strftime.c (revision 7c1d35456b77b833f089b886b99aa431a24e1377)
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
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 *
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 *
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 *
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