xref: /original-bsd/lib/libc/string/strftime.c (revision 1e82c8c9)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #if defined(LIBC_SCCS) && !defined(lint)
19 static char sccsid[] = "@(#)strftime.c	5.7 (Berkeley) 05/17/90";
20 #endif /* LIBC_SCCS and not lint */
21 
22 #include <sys/types.h>
23 #include <sys/time.h>
24 #include <tzfile.h>
25 #include <string.h>
26 
27 static char *afmt[] = {
28 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
29 };
30 static char *Afmt[] = {
31 	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
32 	"Saturday",
33 };
34 static char *bfmt[] = {
35 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
36 	"Oct", "Nov", "Dec",
37 };
38 static char *Bfmt[] = {
39 	"January", "February", "March", "April", "May", "June", "July",
40 	"August", "September", "October", "November", "December",
41 };
42 
43 static size_t gsize;
44 static char *pt;
45 
46 size_t
47 strftime(s, maxsize, format, t)
48 	char *s;
49 	char *format;
50 	size_t maxsize;
51 	struct tm *t;
52 {
53 	size_t _fmt();
54 
55 	pt = s;
56 	if ((gsize = maxsize) < 1)
57 		return(0);
58 	if (_fmt(format, t)) {
59 		*pt = '\0';
60 		return(maxsize - gsize);
61 	}
62 	return(0);
63 }
64 
65 static size_t
66 _fmt(format, t)
67 	register char *format;
68 	struct tm *t;
69 {
70 	for (; *format; ++format) {
71 		if (*format == '%')
72 			switch(*++format) {
73 			case '\0':
74 				--format;
75 				break;
76 			case 'A':
77 				if (t->tm_wday < 0 || t->tm_wday > 6)
78 					return(0);
79 				if (!_add(Afmt[t->tm_wday]))
80 					return(0);
81 				continue;
82 			case 'a':
83 				if (t->tm_wday < 0 || t->tm_wday > 6)
84 					return(0);
85 				if (!_add(afmt[t->tm_wday]))
86 					return(0);
87 				continue;
88 			case 'B':
89 				if (t->tm_mon < 0 || t->tm_mon > 11)
90 					return(0);
91 				if (!_add(Bfmt[t->tm_mon]))
92 					return(0);
93 				continue;
94 			case 'b':
95 			case 'h':
96 				if (t->tm_mon < 0 || t->tm_mon > 11)
97 					return(0);
98 				if (!_add(bfmt[t->tm_mon]))
99 					return(0);
100 				continue;
101 			case 'C':
102 				if (!_fmt("%a %b %e %H:%M:%S %Y", t))
103 					return(0);
104 				continue;
105 			case 'c':
106 				if (!_fmt("%m/%d/%y %H:%M:%S", t))
107 					return(0);
108 				continue;
109 			case 'e':
110 				if (!_conv(t->tm_mday, 2, ' '))
111 					return(0);
112 				continue;
113 			case 'D':
114 				if (!_fmt("%m/%d/%y", t))
115 					return(0);
116 				continue;
117 			case 'd':
118 				if (!_conv(t->tm_mday, 2, '0'))
119 					return(0);
120 				continue;
121 			case 'H':
122 				if (!_conv(t->tm_hour, 2, '0'))
123 					return(0);
124 				continue;
125 			case 'I':
126 				if (!_conv(t->tm_hour % 12 ?
127 				    t->tm_hour % 12 : 12, 2, '0'))
128 					return(0);
129 				continue;
130 			case 'j':
131 				if (!_conv(t->tm_yday + 1, 3, '0'))
132 					return(0);
133 				continue;
134 			case 'k':
135 				if (!_conv(t->tm_hour, 2, ' '))
136 					return(0);
137 				continue;
138 			case 'l':
139 				if (!_conv(t->tm_hour % 12 ?
140 				    t->tm_hour % 12 : 12, 2, ' '))
141 					return(0);
142 				continue;
143 			case 'M':
144 				if (!_conv(t->tm_min, 2, '0'))
145 					return(0);
146 				continue;
147 			case 'm':
148 				if (!_conv(t->tm_mon + 1, 2, '0'))
149 					return(0);
150 				continue;
151 			case 'n':
152 				if (!_add("\n"))
153 					return(0);
154 				continue;
155 			case 'p':
156 				if (!_add(t->tm_hour >= 12 ? "PM" : "AM"))
157 					return(0);
158 				continue;
159 			case 'R':
160 				if (!_fmt("%H:%M", t))
161 					return(0);
162 				continue;
163 			case 'r':
164 				if (!_fmt("%I:%M:%S %p", t))
165 					return(0);
166 				continue;
167 			case 'S':
168 				if (!_conv(t->tm_sec, 2, '0'))
169 					return(0);
170 				continue;
171 			case 'T':
172 			case 'X':
173 				if (!_fmt("%H:%M:%S", t))
174 					return(0);
175 				continue;
176 			case 't':
177 				if (!_add("\t"))
178 					return(0);
179 				continue;
180 			case 'U':
181 				if (!_conv((t->tm_yday + 7 - t->tm_wday) / 7,
182 				    2, '0'))
183 					return(0);
184 				continue;
185 			case 'W':
186 				if (!_conv((t->tm_yday + 7 -
187 				    (t->tm_wday ? (t->tm_wday - 1) : 6))
188 				    / 7, 2, '0'))
189 					return(0);
190 				continue;
191 			case 'w':
192 				if (!_conv(t->tm_wday, 1, '0'))
193 					return(0);
194 				continue;
195 			case 'x':
196 				if (!_fmt("%m/%d/%y", t))
197 					return(0);
198 				continue;
199 			case 'y':
200 				if (!_conv((t->tm_year + TM_YEAR_BASE)
201 				    % 100, 2, '0'))
202 					return(0);
203 				continue;
204 			case 'Y':
205 				if (!_conv(t->tm_year + TM_YEAR_BASE, 4, '0'))
206 					return(0);
207 				continue;
208 			case 'Z':
209 				if (!t->tm_zone || !_add(t->tm_zone))
210 					return(0);
211 				continue;
212 			case '%':
213 			/*
214 			 * X311J/88-090 (4.12.3.5): if conversion char is
215 			 * undefined, behavior is undefined.  Print out the
216 			 * character itself as printf(3) does.
217 			 */
218 			default:
219 				break;
220 		}
221 		if (!gsize--)
222 			return(0);
223 		*pt++ = *format;
224 	}
225 	return(gsize);
226 }
227 
228 static
229 _conv(n, digits, pad)
230 	int n, digits;
231 	char pad;
232 {
233 	static char buf[10];
234 	register char *p;
235 
236 	for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits)
237 		*p-- = n % 10 + '0';
238 	while (p > buf && digits-- > 0)
239 		*p-- = pad;
240 	return(_add(++p));
241 }
242 
243 static
244 _add(str)
245 	register char *str;
246 {
247 	for (;; ++pt, --gsize) {
248 		if (!gsize)
249 			return(0);
250 		if (!(*pt = *str++))
251 			return(1);
252 	}
253 }
254