1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright (C) 2007 Denys Vlasenko
6  *
7  * Licensed under GPLv2, see file LICENSE in this source tree.
8  */
9 #include "libbb.h"
10 
parse_datestr(const char * date_str,struct tm * ptm)11 void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
12 {
13 	char end = '\0';
14 	const char *last_colon = strrchr(date_str, ':');
15 
16 	if (last_colon != NULL) {
17 		/* Parse input and assign appropriately to ptm */
18 #if ENABLE_DESKTOP
19 		const char *endp;
20 #endif
21 
22 		/* HH:MM */
23 		if (sscanf(date_str, "%u:%u%c",
24 					&ptm->tm_hour,
25 					&ptm->tm_min,
26 					&end) >= 2
27 		) {
28 			/* no adjustments needed */
29 		} else
30 		/* mm.dd-HH:MM */
31 		if (sscanf(date_str, "%u.%u-%u:%u%c",
32 					&ptm->tm_mon, &ptm->tm_mday,
33 					&ptm->tm_hour, &ptm->tm_min,
34 					&end) >= 4
35 		) {
36 			/* Adjust month from 1-12 to 0-11 */
37 			ptm->tm_mon -= 1;
38 		} else
39 		/* yyyy.mm.dd-HH:MM */
40 		if (sscanf(date_str, "%u.%u.%u-%u:%u%c", &ptm->tm_year,
41 					&ptm->tm_mon, &ptm->tm_mday,
42 					&ptm->tm_hour, &ptm->tm_min,
43 					&end) >= 5
44 		/* yyyy-mm-dd HH:MM */
45 		 || sscanf(date_str, "%u-%u-%u %u:%u%c", &ptm->tm_year,
46 					&ptm->tm_mon, &ptm->tm_mday,
47 					&ptm->tm_hour, &ptm->tm_min,
48 					&end) >= 5
49 		) {
50 			ptm->tm_year -= 1900; /* Adjust years */
51 			ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
52 		} else
53 #if ENABLE_DESKTOP  /* strptime is BIG: ~1k in uclibc, ~10k in glibc */
54 		/* month_name d HH:MM:SS YYYY. Supported by GNU date */
55 		if ((endp = strptime(date_str, "%b %d %T %Y", ptm)) != NULL
56 		 && *endp == '\0'
57 		) {
58 			return; /* don't fall through to end == ":" check */
59 		} else
60 #endif
61 		{
62 			bb_error_msg_and_die(bb_msg_invalid_date, date_str);
63 		}
64 		if (end == ':') {
65 			/* xxx:SS */
66 			if (sscanf(last_colon + 1, "%u%c", &ptm->tm_sec, &end) == 1)
67 				end = '\0';
68 			/* else end != NUL and we error out */
69 		}
70 	} else
71 	if (strchr(date_str, '-')
72 	    /* Why strchr('-') check?
73 	     * sscanf below will trash ptm->tm_year, this breaks
74 	     * if parse_str is "10101010" (iow, "MMddhhmm" form)
75 	     * because we destroy year. Do these sscanf
76 	     * only if we saw a dash in parse_str.
77 	     */
78 		/* yyyy-mm-dd HH */
79 	 && (sscanf(date_str, "%u-%u-%u %u%c", &ptm->tm_year,
80 				&ptm->tm_mon, &ptm->tm_mday,
81 				&ptm->tm_hour,
82 				&end) >= 4
83 		/* yyyy-mm-dd */
84 	     || sscanf(date_str, "%u-%u-%u%c", &ptm->tm_year,
85 				&ptm->tm_mon, &ptm->tm_mday,
86 				&end) >= 3
87 	    )
88 	) {
89 		ptm->tm_year -= 1900; /* Adjust years */
90 		ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
91 	} else
92 	if (date_str[0] == '@') {
93 		time_t t = bb_strtol(date_str + 1, NULL, 10);
94 		if (!errno) {
95 			struct tm *lt = localtime(&t);
96 			if (lt) {
97 				*ptm = *lt;
98 				return;
99 			}
100 		}
101 		end = '1';
102 	} else {
103 		/* Googled the following on an old date manpage:
104 		 *
105 		 * The canonical representation for setting the date/time is:
106 		 * cc   Century (either 19 or 20)
107 		 * yy   Year in abbreviated form (e.g. 89, 06)
108 		 * mm   Numeric month, a number from 1 to 12
109 		 * dd   Day, a number from 1 to 31
110 		 * HH   Hour, a number from 0 to 23
111 		 * MM   Minutes, a number from 0 to 59
112 		 * .SS  Seconds, a number from 0 to 61 (with leap seconds)
113 		 * Everything but the minutes is optional
114 		 *
115 		 * "touch -t DATETIME" format: [[[[[YY]YY]MM]DD]hh]mm[.ss]
116 		 * Some, but not all, Unix "date DATETIME" commands
117 		 * move [[YY]YY] past minutes mm field (!).
118 		 * Coreutils date does it, and SUS mandates it.
119 		 * (date -s DATETIME does not support this format. lovely!)
120 		 * In bbox, this format is special-cased in date applet
121 		 * (IOW: this function assumes "touch -t" format).
122 		 */
123 		unsigned cur_year = ptm->tm_year;
124 		int len = strchrnul(date_str, '.') - date_str;
125 
126 		/* MM[.SS] */
127 		if (len == 2 && sscanf(date_str, "%2u%2u%2u%2u""%2u%c" + 12,
128 					&ptm->tm_min,
129 					&end) >= 1) {
130 		} else
131 		/* HHMM[.SS] */
132 		if (len == 4 && sscanf(date_str, "%2u%2u%2u""%2u%2u%c" + 9,
133 					&ptm->tm_hour,
134 					&ptm->tm_min,
135 					&end) >= 2) {
136 		} else
137 		/* ddHHMM[.SS] */
138 		if (len == 6 && sscanf(date_str, "%2u%2u""%2u%2u%2u%c" + 6,
139 					&ptm->tm_mday,
140 					&ptm->tm_hour,
141 					&ptm->tm_min,
142 					&end) >= 3) {
143 		} else
144 		/* mmddHHMM[.SS] */
145 		if (len == 8 && sscanf(date_str, "%2u""%2u%2u%2u%2u%c" + 3,
146 					&ptm->tm_mon,
147 					&ptm->tm_mday,
148 					&ptm->tm_hour,
149 					&ptm->tm_min,
150 					&end) >= 4) {
151 			/* Adjust month from 1-12 to 0-11 */
152 			ptm->tm_mon -= 1;
153 		} else
154 		/* yymmddHHMM[.SS] */
155 		if (len == 10 && sscanf(date_str, "%2u%2u%2u%2u%2u%c",
156 					&ptm->tm_year,
157 					&ptm->tm_mon,
158 					&ptm->tm_mday,
159 					&ptm->tm_hour,
160 					&ptm->tm_min,
161 					&end) >= 5) {
162 			/* Adjust month from 1-12 to 0-11 */
163 			ptm->tm_mon -= 1;
164 			if ((int)cur_year >= 50) { /* >= 1950 */
165 				/* Adjust year: */
166 				/* 1. Put it in the current century */
167 				ptm->tm_year += (cur_year / 100) * 100;
168 				/* 2. If too far in the past, +100 years */
169 				if (ptm->tm_year < cur_year - 50)
170 					ptm->tm_year += 100;
171 				/* 3. If too far in the future, -100 years */
172 				if (ptm->tm_year > cur_year + 50)
173 					ptm->tm_year -= 100;
174 			}
175 		} else
176 		/* ccyymmddHHMM[.SS] */
177 		if (len == 12 && sscanf(date_str, "%4u%2u%2u%2u%2u%c",
178 					&ptm->tm_year,
179 					&ptm->tm_mon,
180 					&ptm->tm_mday,
181 					&ptm->tm_hour,
182 					&ptm->tm_min,
183 					&end) >= 5) {
184 			ptm->tm_year -= 1900; /* Adjust years */
185 			ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
186 		} else {
187 			bb_error_msg_and_die(bb_msg_invalid_date, date_str);
188 		}
189 		ptm->tm_sec = 0; /* assume zero if [.SS] is not given */
190 		if (end == '.') {
191 			/* xxx.SS */
192 			if (sscanf(strchr(date_str, '.') + 1, "%u%c",
193 					&ptm->tm_sec, &end) == 1)
194 				end = '\0';
195 			/* else end != NUL and we error out */
196 		}
197 	}
198 	if (end != '\0') {
199 		bb_error_msg_and_die(bb_msg_invalid_date, date_str);
200 	}
201 }
202 
validate_tm_time(const char * date_str,struct tm * ptm)203 time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *ptm)
204 {
205 	time_t t = mktime(ptm);
206 	if (t == (time_t) -1L) {
207 		bb_error_msg_and_die(bb_msg_invalid_date, date_str);
208 	}
209 	return t;
210 }
211 
strftime_fmt(char * buf,unsigned len,time_t * tp,const char * fmt)212 static char* strftime_fmt(char *buf, unsigned len, time_t *tp, const char *fmt)
213 {
214 	time_t t;
215 	if (!tp) {
216 		tp = &t;
217 		time(tp);
218 	}
219 	/* Returns pointer to NUL */
220 	return buf + strftime(buf, len, fmt, localtime(tp));
221 }
222 
strftime_HHMMSS(char * buf,unsigned len,time_t * tp)223 char* FAST_FUNC strftime_HHMMSS(char *buf, unsigned len, time_t *tp)
224 {
225 	return strftime_fmt(buf, len, tp, "%H:%M:%S");
226 }
227 
strftime_YYYYMMDDHHMMSS(char * buf,unsigned len,time_t * tp)228 char* FAST_FUNC strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp)
229 {
230 	return strftime_fmt(buf, len, tp, "%Y-%m-%d %H:%M:%S");
231 }
232 
233 #if ENABLE_MONOTONIC_SYSCALL
234 
235 #include <sys/syscall.h>
236 /* Old glibc (< 2.3.4) does not provide this constant. We use syscall
237  * directly so this definition is safe. */
238 #ifndef CLOCK_MONOTONIC
239 #define CLOCK_MONOTONIC 1
240 #endif
241 
242 /* libc has incredibly messy way of doing this,
243  * typically requiring -lrt. We just skip all this mess */
get_mono(struct timespec * ts)244 static void get_mono(struct timespec *ts)
245 {
246 	if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts))
247 		bb_error_msg_and_die("clock_gettime(MONOTONIC) failed");
248 }
monotonic_ns(void)249 unsigned long long FAST_FUNC monotonic_ns(void)
250 {
251 	struct timespec ts;
252 	get_mono(&ts);
253 	return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
254 }
monotonic_us(void)255 unsigned long long FAST_FUNC monotonic_us(void)
256 {
257 	struct timespec ts;
258 	get_mono(&ts);
259 	return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000;
260 }
monotonic_ms(void)261 unsigned long long FAST_FUNC monotonic_ms(void)
262 {
263 	struct timespec ts;
264 	get_mono(&ts);
265 	return ts.tv_sec * 1000ULL + ts.tv_nsec/1000000;
266 }
monotonic_sec(void)267 unsigned FAST_FUNC monotonic_sec(void)
268 {
269 	struct timespec ts;
270 	get_mono(&ts);
271 	return ts.tv_sec;
272 }
273 
274 #else
275 
monotonic_ns(void)276 unsigned long long FAST_FUNC monotonic_ns(void)
277 {
278 	struct timeval tv;
279 	gettimeofday(&tv, NULL);
280 	return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
281 }
monotonic_us(void)282 unsigned long long FAST_FUNC monotonic_us(void)
283 {
284 	struct timeval tv;
285 	gettimeofday(&tv, NULL);
286 	return tv.tv_sec * 1000000ULL + tv.tv_usec;
287 }
monotonic_ms(void)288 unsigned long long FAST_FUNC monotonic_ms(void)
289 {
290 	struct timeval tv;
291 	gettimeofday(&tv, NULL);
292 	return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
293 }
monotonic_sec(void)294 unsigned FAST_FUNC monotonic_sec(void)
295 {
296 	return time(NULL);
297 }
298 
299 #endif
300