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