1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "analyze.h"
4 #include "analyze-calendar.h"
5 #include "calendarspec.h"
6 #include "format-table.h"
7 #include "terminal-util.h"
8
test_calendar_one(usec_t n,const char * p)9 static int test_calendar_one(usec_t n, const char *p) {
10 _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
11 _cleanup_(table_unrefp) Table *table = NULL;
12 _cleanup_free_ char *t = NULL;
13 TableCell *cell;
14 int r;
15
16 r = calendar_spec_from_string(p, &spec);
17 if (r < 0) {
18 log_error_errno(r, "Failed to parse calendar specification '%s': %m", p);
19 time_parsing_hint(p, /* calendar= */ false, /* timestamp= */ true, /* timespan= */ true);
20 return r;
21 }
22
23 r = calendar_spec_to_string(spec, &t);
24 if (r < 0)
25 return log_error_errno(r, "Failed to format calendar specification '%s': %m", p);
26
27 table = table_new("name", "value");
28 if (!table)
29 return log_oom();
30
31 table_set_header(table, false);
32
33 assert_se(cell = table_get_cell(table, 0, 0));
34 r = table_set_ellipsize_percent(table, cell, 100);
35 if (r < 0)
36 return r;
37
38 r = table_set_align_percent(table, cell, 100);
39 if (r < 0)
40 return r;
41
42 assert_se(cell = table_get_cell(table, 0, 1));
43 r = table_set_ellipsize_percent(table, cell, 100);
44 if (r < 0)
45 return r;
46
47 if (!streq(t, p)) {
48 r = table_add_many(table,
49 TABLE_STRING, "Original form:",
50 TABLE_STRING, p);
51 if (r < 0)
52 return table_log_add_error(r);
53 }
54
55 r = table_add_many(table,
56 TABLE_STRING, "Normalized form:",
57 TABLE_STRING, t);
58 if (r < 0)
59 return table_log_add_error(r);
60
61 for (unsigned i = 0; i < arg_iterations; i++) {
62 usec_t next;
63
64 r = calendar_spec_next_usec(spec, n, &next);
65 if (r == -ENOENT) {
66 if (i == 0) {
67 r = table_add_many(table,
68 TABLE_STRING, "Next elapse:",
69 TABLE_STRING, "never",
70 TABLE_SET_COLOR, ansi_highlight_yellow());
71 if (r < 0)
72 return table_log_add_error(r);
73 }
74 break;
75 }
76 if (r < 0)
77 return log_error_errno(r, "Failed to determine next elapse for '%s': %m", p);
78
79 if (i == 0) {
80 r = table_add_many(table,
81 TABLE_STRING, "Next elapse:",
82 TABLE_TIMESTAMP, next,
83 TABLE_SET_COLOR, ansi_highlight_blue());
84 if (r < 0)
85 return table_log_add_error(r);
86 } else {
87 int k = DECIMAL_STR_WIDTH(i + 1);
88
89 if (k < 8)
90 k = 8 - k;
91 else
92 k = 0;
93
94 r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1);
95 if (r < 0)
96 return table_log_add_error(r);
97
98 r = table_add_many(table,
99 TABLE_TIMESTAMP, next,
100 TABLE_SET_COLOR, ansi_highlight_blue());
101 if (r < 0)
102 return table_log_add_error(r);
103 }
104
105 if (!in_utc_timezone()) {
106 r = table_add_many(table,
107 TABLE_STRING, "(in UTC):",
108 TABLE_TIMESTAMP_UTC, next);
109 if (r < 0)
110 return table_log_add_error(r);
111 }
112
113 r = table_add_many(table,
114 TABLE_STRING, "From now:",
115 TABLE_TIMESTAMP_RELATIVE, next);
116 if (r < 0)
117 return table_log_add_error(r);
118
119 n = next;
120 }
121
122 return table_print(table, NULL);
123 }
124
verb_calendar(int argc,char * argv[],void * userdata)125 int verb_calendar(int argc, char *argv[], void *userdata) {
126 int ret = 0, r;
127 usec_t n;
128
129 if (arg_base_time != USEC_INFINITY)
130 n = arg_base_time;
131 else
132 n = now(CLOCK_REALTIME); /* We want to use the same "base" for all expressions */
133
134 STRV_FOREACH(p, strv_skip(argv, 1)) {
135 r = test_calendar_one(n, *p);
136 if (ret == 0 && r < 0)
137 ret = r;
138
139 if (*(p + 1))
140 putchar('\n');
141 }
142
143 return ret;
144 }
145