]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
api: unify some common bits used by public systemd APIs
[thirdparty/systemd.git] / src / shared / time-util.c
CommitLineData
9a98c7a1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <time.h>
23#include <string.h>
24
25#include "util.h"
26#include "time-util.h"
27
28usec_t now(clockid_t clock_id) {
29 struct timespec ts;
30
31 assert_se(clock_gettime(clock_id, &ts) == 0);
32
33 return timespec_load(&ts);
34}
35
36dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
37 assert(ts);
38
39 ts->realtime = now(CLOCK_REALTIME);
40 ts->monotonic = now(CLOCK_MONOTONIC);
41
42 return ts;
43}
44
45dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
46 int64_t delta;
47 assert(ts);
48
cae0c5e0
LP
49 if (u == (usec_t) -1) {
50 ts->realtime = ts->monotonic = (usec_t) -1;
51 return ts;
52 }
53
9a98c7a1
LP
54 ts->realtime = u;
55
56 if (u == 0)
57 ts->monotonic = 0;
58 else {
59 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
60
61 ts->monotonic = now(CLOCK_MONOTONIC);
62
63 if ((int64_t) ts->monotonic > delta)
64 ts->monotonic -= delta;
65 else
66 ts->monotonic = 0;
67 }
68
69 return ts;
70}
71
cae0c5e0
LP
72dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
73 int64_t delta;
74 assert(ts);
75
76 if (u == (usec_t) -1) {
77 ts->realtime = ts->monotonic = (usec_t) -1;
78 return ts;
79 }
80
81 ts->monotonic = u;
82 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
83
84 ts->realtime = now(CLOCK_REALTIME);
85 if ((int64_t) ts->realtime > delta)
86 ts->realtime -= delta;
87 else
88 ts->realtime = 0;
89
90 return ts;
91}
92
9a98c7a1
LP
93usec_t timespec_load(const struct timespec *ts) {
94 assert(ts);
95
96 if (ts->tv_sec == (time_t) -1 &&
97 ts->tv_nsec == (long) -1)
98 return (usec_t) -1;
99
100 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
101 return (usec_t) -1;
102
103 return
104 (usec_t) ts->tv_sec * USEC_PER_SEC +
105 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
106}
107
108struct timespec *timespec_store(struct timespec *ts, usec_t u) {
109 assert(ts);
110
111 if (u == (usec_t) -1) {
112 ts->tv_sec = (time_t) -1;
113 ts->tv_nsec = (long) -1;
114 return ts;
115 }
116
117 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
118 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
119
120 return ts;
121}
122
123usec_t timeval_load(const struct timeval *tv) {
124 assert(tv);
125
126 if (tv->tv_sec == (time_t) -1 &&
127 tv->tv_usec == (suseconds_t) -1)
128 return (usec_t) -1;
129
130 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
131 return (usec_t) -1;
132
133 return
134 (usec_t) tv->tv_sec * USEC_PER_SEC +
135 (usec_t) tv->tv_usec;
136}
137
138struct timeval *timeval_store(struct timeval *tv, usec_t u) {
139 assert(tv);
140
141 if (u == (usec_t) -1) {
142 tv->tv_sec = (time_t) -1;
143 tv->tv_usec = (suseconds_t) -1;
144 return tv;
145 }
146
147 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
148 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
149
150 return tv;
151}
152
153char *format_timestamp(char *buf, size_t l, usec_t t) {
154 struct tm tm;
155 time_t sec;
156
157 assert(buf);
158 assert(l > 0);
159
160 if (t <= 0)
161 return NULL;
162
163 sec = (time_t) (t / USEC_PER_SEC);
164
165 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
166 return NULL;
167
168 return buf;
169}
170
f02d8367
ZJS
171char *format_timestamp_us(char *buf, size_t l, usec_t t) {
172 struct tm tm;
173 time_t sec;
174
175 assert(buf);
176 assert(l > 0);
177
178 if (t <= 0)
179 return NULL;
180
181 sec = (time_t) (t / USEC_PER_SEC);
182 localtime_r(&sec, &tm);
183
184 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
185 return NULL;
186 snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", t % USEC_PER_SEC);
187 if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
188 return NULL;
189
190 return buf;
191}
192
bbb8486e 193char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
9a98c7a1
LP
194 usec_t n, d;
195
196 n = now(CLOCK_REALTIME);
197
198 if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
199 return NULL;
200
201 d = n - t;
202
203 if (d >= USEC_PER_YEAR)
204 snprintf(buf, l, "%llu years %llu months ago",
205 (unsigned long long) (d / USEC_PER_YEAR),
206 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
207 else if (d >= USEC_PER_MONTH)
208 snprintf(buf, l, "%llu months %llu days ago",
209 (unsigned long long) (d / USEC_PER_MONTH),
210 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
211 else if (d >= USEC_PER_WEEK)
212 snprintf(buf, l, "%llu weeks %llu days ago",
213 (unsigned long long) (d / USEC_PER_WEEK),
214 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
215 else if (d >= 2*USEC_PER_DAY)
216 snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
217 else if (d >= 25*USEC_PER_HOUR)
218 snprintf(buf, l, "1 day %lluh ago",
219 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
220 else if (d >= 6*USEC_PER_HOUR)
221 snprintf(buf, l, "%lluh ago",
222 (unsigned long long) (d / USEC_PER_HOUR));
223 else if (d >= USEC_PER_HOUR)
224 snprintf(buf, l, "%lluh %llumin ago",
225 (unsigned long long) (d / USEC_PER_HOUR),
226 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
227 else if (d >= 5*USEC_PER_MINUTE)
228 snprintf(buf, l, "%llumin ago",
229 (unsigned long long) (d / USEC_PER_MINUTE));
230 else if (d >= USEC_PER_MINUTE)
231 snprintf(buf, l, "%llumin %llus ago",
232 (unsigned long long) (d / USEC_PER_MINUTE),
233 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
234 else if (d >= USEC_PER_SEC)
235 snprintf(buf, l, "%llus ago",
236 (unsigned long long) (d / USEC_PER_SEC));
237 else if (d >= USEC_PER_MSEC)
238 snprintf(buf, l, "%llums ago",
239 (unsigned long long) (d / USEC_PER_MSEC));
240 else if (d > 0)
241 snprintf(buf, l, "%lluus ago",
242 (unsigned long long) d);
243 else
244 snprintf(buf, l, "now");
245
246 buf[l-1] = 0;
247 return buf;
248}
249
2fa4092c 250char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
251 static const struct {
252 const char *suffix;
253 usec_t usec;
254 } table[] = {
b719810d
LP
255 { "y", USEC_PER_YEAR },
256 { "month", USEC_PER_MONTH },
9a98c7a1
LP
257 { "w", USEC_PER_WEEK },
258 { "d", USEC_PER_DAY },
259 { "h", USEC_PER_HOUR },
260 { "min", USEC_PER_MINUTE },
261 { "s", USEC_PER_SEC },
262 { "ms", USEC_PER_MSEC },
263 { "us", 1 },
264 };
265
266 unsigned i;
267 char *p = buf;
2fa4092c 268 bool something = false;
9a98c7a1
LP
269
270 assert(buf);
271 assert(l > 0);
272
273 if (t == (usec_t) -1)
274 return NULL;
275
7c537b2e
LP
276 if (t <= 0) {
277 snprintf(p, l, "0");
278 p[l-1] = 0;
279 return p;
280 }
281
7f602784 282 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
283
284 for (i = 0; i < ELEMENTSOF(table); i++) {
285 int k;
286 size_t n;
2fa4092c
LP
287 bool done = false;
288 usec_t a, b;
289
7c537b2e
LP
290 if (t <= 0)
291 break;
2fa4092c 292
7c537b2e 293 if (t < accuracy && something)
2fa4092c 294 break;
9a98c7a1
LP
295
296 if (t < table[i].usec)
297 continue;
298
299 if (l <= 1)
300 break;
301
2fa4092c
LP
302 a = t / table[i].usec;
303 b = t % table[i].usec;
304
305 /* Let's see if we should shows this in dot notation */
306 if (t < USEC_PER_MINUTE && b > 0) {
307 usec_t cc;
308 int j;
309
310 j = 0;
311 for (cc = table[i].usec; cc > 1; cc /= 10)
312 j++;
313
314 for (cc = accuracy; cc > 1; cc /= 10) {
315 b /= 10;
316 j--;
317 }
318
319 if (j > 0) {
320 k = snprintf(p, l,
321 "%s%llu.%0*llu%s",
322 p > buf ? " " : "",
323 (unsigned long long) a,
324 j,
325 (unsigned long long) b,
326 table[i].suffix);
327
328 t = 0;
329 done = true;
330 }
331 }
332
333 /* No? Then let's show it normally */
334 if (!done) {
335 k = snprintf(p, l,
336 "%s%llu%s",
337 p > buf ? " " : "",
338 (unsigned long long) a,
339 table[i].suffix);
340
341 t = b;
342 }
343
9a98c7a1
LP
344 n = MIN((size_t) k, l);
345
346 l -= n;
347 p += n;
348
2fa4092c 349 something = true;
9a98c7a1
LP
350 }
351
352 *p = 0;
353
354 return buf;
355}
356
357void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
358
359 assert(f);
360 assert(name);
361 assert(t);
362
363 if (!dual_timestamp_is_set(t))
364 return;
365
366 fprintf(f, "%s=%llu %llu\n",
367 name,
368 (unsigned long long) t->realtime,
369 (unsigned long long) t->monotonic);
370}
371
372void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
373 unsigned long long a, b;
374
375 assert(value);
376 assert(t);
377
378 if (sscanf(value, "%lli %llu", &a, &b) != 2)
379 log_debug("Failed to parse finish timestamp value %s", value);
380 else {
381 t->realtime = a;
382 t->monotonic = b;
383 }
384}
385
386int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
387 static const struct {
388 const char *name;
389 const int nr;
390 } day_nr[] = {
391 { "Sunday", 0 },
392 { "Sun", 0 },
393 { "Monday", 1 },
394 { "Mon", 1 },
395 { "Tuesday", 2 },
396 { "Tue", 2 },
397 { "Wednesday", 3 },
398 { "Wed", 3 },
399 { "Thursday", 4 },
400 { "Thu", 4 },
401 { "Friday", 5 },
402 { "Fri", 5 },
403 { "Saturday", 6 },
404 { "Sat", 6 },
405 };
406
9a98c7a1
LP
407 const char *k;
408 struct tm tm, copy;
409 time_t x;
410 usec_t plus = 0, minus = 0, ret;
92134489
LP
411 int r, weekday = -1;
412 unsigned i;
9a98c7a1
LP
413
414 /*
415 * Allowed syntaxes:
416 *
417 * 2012-09-22 16:34:22
418 * 2012-09-22 16:34 (seconds will be set to 0)
419 * 2012-09-22 (time will be set to 00:00:00)
420 * 16:34:22 (date will be set to today)
421 * 16:34 (date will be set to today, seconds to 0)
422 * now
423 * yesterday (time is set to 00:00:00)
424 * today (time is set to 00:00:00)
425 * tomorrow (time is set to 00:00:00)
426 * +5min
427 * -5days
428 *
429 */
430
431 assert(t);
432 assert(usec);
433
434 x = time(NULL);
435 assert_se(localtime_r(&x, &tm));
6a741b4a 436 tm.tm_isdst = -1;
9a98c7a1
LP
437
438 if (streq(t, "now"))
439 goto finish;
440
441 else if (streq(t, "today")) {
442 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
443 goto finish;
444
445 } else if (streq(t, "yesterday")) {
446 tm.tm_mday --;
447 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
448 goto finish;
449
450 } else if (streq(t, "tomorrow")) {
451 tm.tm_mday ++;
452 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
453 goto finish;
454
455 } else if (t[0] == '+') {
456
7f602784 457 r = parse_sec(t+1, &plus);
9a98c7a1
LP
458 if (r < 0)
459 return r;
460
461 goto finish;
462 } else if (t[0] == '-') {
463
7f602784 464 r = parse_sec(t+1, &minus);
9a98c7a1
LP
465 if (r < 0)
466 return r;
467
468 goto finish;
decad910
LP
469
470 } else if (endswith(t, " ago")) {
471 _cleanup_free_ char *z;
472
473 z = strndup(t, strlen(t) - 4);
474 if (!z)
475 return -ENOMEM;
476
7f602784 477 r = parse_sec(z, &minus);
decad910
LP
478 if (r < 0)
479 return r;
480
481 goto finish;
9a98c7a1
LP
482 }
483
92134489
LP
484 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
485 size_t skip;
486
487 if (!startswith_no_case(t, day_nr[i].name))
488 continue;
489
490 skip = strlen(day_nr[i].name);
491 if (t[skip] != ' ')
492 continue;
493
494 weekday = day_nr[i].nr;
495 t += skip + 1;
496 break;
497 }
498
9a98c7a1
LP
499 copy = tm;
500 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
501 if (k && *k == 0)
502 goto finish;
503
504 tm = copy;
505 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
506 if (k && *k == 0)
507 goto finish;
508
509 tm = copy;
510 k = strptime(t, "%y-%m-%d %H:%M", &tm);
511 if (k && *k == 0) {
512 tm.tm_sec = 0;
513 goto finish;
514 }
515
516 tm = copy;
517 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
518 if (k && *k == 0) {
519 tm.tm_sec = 0;
520 goto finish;
521 }
522
523 tm = copy;
524 k = strptime(t, "%y-%m-%d", &tm);
525 if (k && *k == 0) {
526 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
527 goto finish;
528 }
529
530 tm = copy;
531 k = strptime(t, "%Y-%m-%d", &tm);
532 if (k && *k == 0) {
533 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
534 goto finish;
535 }
536
537 tm = copy;
538 k = strptime(t, "%H:%M:%S", &tm);
539 if (k && *k == 0)
540 goto finish;
541
542 tm = copy;
543 k = strptime(t, "%H:%M", &tm);
544 if (k && *k == 0) {
545 tm.tm_sec = 0;
546 goto finish;
547 }
548
549 return -EINVAL;
550
551finish:
552 x = mktime(&tm);
553 if (x == (time_t) -1)
554 return -EINVAL;
555
92134489
LP
556 if (weekday >= 0 && tm.tm_wday != weekday)
557 return -EINVAL;
558
9a98c7a1
LP
559 ret = (usec_t) x * USEC_PER_SEC;
560
561 ret += plus;
562 if (ret > minus)
563 ret -= minus;
564 else
565 ret = 0;
566
567 *usec = ret;
568
569 return 0;
570}
571
7f602784 572int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
573 static const struct {
574 const char *suffix;
575 usec_t usec;
576 } table[] = {
577 { "seconds", USEC_PER_SEC },
578 { "second", USEC_PER_SEC },
579 { "sec", USEC_PER_SEC },
580 { "s", USEC_PER_SEC },
581 { "minutes", USEC_PER_MINUTE },
582 { "minute", USEC_PER_MINUTE },
583 { "min", USEC_PER_MINUTE },
584 { "months", USEC_PER_MONTH },
585 { "month", USEC_PER_MONTH },
586 { "msec", USEC_PER_MSEC },
587 { "ms", USEC_PER_MSEC },
588 { "m", USEC_PER_MINUTE },
589 { "hours", USEC_PER_HOUR },
590 { "hour", USEC_PER_HOUR },
591 { "hr", USEC_PER_HOUR },
592 { "h", USEC_PER_HOUR },
593 { "days", USEC_PER_DAY },
594 { "day", USEC_PER_DAY },
595 { "d", USEC_PER_DAY },
596 { "weeks", USEC_PER_WEEK },
597 { "week", USEC_PER_WEEK },
598 { "w", USEC_PER_WEEK },
599 { "years", USEC_PER_YEAR },
600 { "year", USEC_PER_YEAR },
601 { "y", USEC_PER_YEAR },
602 { "usec", 1ULL },
603 { "us", 1ULL },
604 { "", USEC_PER_SEC }, /* default is sec */
605 };
606
607 const char *p;
608 usec_t r = 0;
cb0dac05 609 bool something = false;
9a98c7a1
LP
610
611 assert(t);
612 assert(usec);
613
614 p = t;
cb0dac05
LP
615 for (;;) {
616 long long l, z = 0;
9a98c7a1 617 char *e;
cb0dac05
LP
618 unsigned i, n = 0;
619
620 p += strspn(p, WHITESPACE);
621
622 if (*p == 0) {
623 if (!something)
624 return -EINVAL;
625
626 break;
627 }
9a98c7a1
LP
628
629 errno = 0;
630 l = strtoll(p, &e, 10);
631
8333c77e 632 if (errno > 0)
9a98c7a1
LP
633 return -errno;
634
635 if (l < 0)
636 return -ERANGE;
637
cb0dac05
LP
638 if (*e == '.') {
639 char *b = e + 1;
640
641 errno = 0;
642 z = strtoll(b, &e, 10);
643 if (errno > 0)
644 return -errno;
645
646 if (z < 0)
647 return -ERANGE;
648
649 if (e == b)
650 return -EINVAL;
651
652 n = e - b;
653
654 } else if (e == p)
9a98c7a1
LP
655 return -EINVAL;
656
657 e += strspn(e, WHITESPACE);
658
659 for (i = 0; i < ELEMENTSOF(table); i++)
660 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
661 usec_t k = (usec_t) z * table[i].usec;
662
663 for (; n > 0; n--)
664 k /= 10;
665
666 r += (usec_t) l * table[i].usec + k;
9a98c7a1 667 p = e + strlen(table[i].suffix);
cb0dac05
LP
668
669 something = true;
9a98c7a1
LP
670 break;
671 }
672
673 if (i >= ELEMENTSOF(table))
674 return -EINVAL;
675
cb0dac05 676 }
9a98c7a1
LP
677
678 *usec = r;
679
680 return 0;
681}
682
683int parse_nsec(const char *t, nsec_t *nsec) {
684 static const struct {
685 const char *suffix;
686 nsec_t nsec;
687 } table[] = {
688 { "seconds", NSEC_PER_SEC },
689 { "second", NSEC_PER_SEC },
690 { "sec", NSEC_PER_SEC },
691 { "s", NSEC_PER_SEC },
692 { "minutes", NSEC_PER_MINUTE },
693 { "minute", NSEC_PER_MINUTE },
694 { "min", NSEC_PER_MINUTE },
695 { "months", NSEC_PER_MONTH },
696 { "month", NSEC_PER_MONTH },
697 { "msec", NSEC_PER_MSEC },
698 { "ms", NSEC_PER_MSEC },
699 { "m", NSEC_PER_MINUTE },
700 { "hours", NSEC_PER_HOUR },
701 { "hour", NSEC_PER_HOUR },
702 { "hr", NSEC_PER_HOUR },
703 { "h", NSEC_PER_HOUR },
704 { "days", NSEC_PER_DAY },
705 { "day", NSEC_PER_DAY },
706 { "d", NSEC_PER_DAY },
707 { "weeks", NSEC_PER_WEEK },
708 { "week", NSEC_PER_WEEK },
709 { "w", NSEC_PER_WEEK },
710 { "years", NSEC_PER_YEAR },
711 { "year", NSEC_PER_YEAR },
712 { "y", NSEC_PER_YEAR },
713 { "usec", NSEC_PER_USEC },
714 { "us", NSEC_PER_USEC },
715 { "nsec", 1ULL },
716 { "ns", 1ULL },
717 { "", 1ULL }, /* default is nsec */
718 };
719
720 const char *p;
721 nsec_t r = 0;
cb0dac05 722 bool something = false;
9a98c7a1
LP
723
724 assert(t);
725 assert(nsec);
726
727 p = t;
cb0dac05
LP
728 for (;;) {
729 long long l, z = 0;
9a98c7a1 730 char *e;
cb0dac05
LP
731 unsigned i, n = 0;
732
733 p += strspn(p, WHITESPACE);
734
735 if (*p == 0) {
736 if (!something)
737 return -EINVAL;
738
739 break;
740 }
9a98c7a1
LP
741
742 errno = 0;
743 l = strtoll(p, &e, 10);
744
8333c77e 745 if (errno > 0)
9a98c7a1
LP
746 return -errno;
747
748 if (l < 0)
749 return -ERANGE;
750
cb0dac05
LP
751 if (*e == '.') {
752 char *b = e + 1;
753
754 errno = 0;
755 z = strtoll(b, &e, 10);
756 if (errno > 0)
757 return -errno;
758
759 if (z < 0)
760 return -ERANGE;
761
762 if (e == b)
763 return -EINVAL;
764
765 n = e - b;
766
767 } else if (e == p)
9a98c7a1
LP
768 return -EINVAL;
769
770 e += strspn(e, WHITESPACE);
771
772 for (i = 0; i < ELEMENTSOF(table); i++)
773 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
774 nsec_t k = (nsec_t) z * table[i].nsec;
775
776 for (; n > 0; n--)
777 k /= 10;
778
779 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 780 p = e + strlen(table[i].suffix);
cb0dac05
LP
781
782 something = true;
9a98c7a1
LP
783 break;
784 }
785
786 if (i >= ELEMENTSOF(table))
787 return -EINVAL;
788
cb0dac05 789 }
9a98c7a1
LP
790
791 *nsec = r;
792
793 return 0;
794}