1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
32 #include "alloc-util.h"
33 #include "calendarspec.h"
36 #include "parse-util.h"
37 #include "string-util.h"
38 #include "time-util.h"
40 #define BITS_WEEKDAYS 127
44 static void free_chain(CalendarComponent
*c
) {
54 void calendar_spec_free(CalendarSpec
*c
) {
63 free_chain(c
->minute
);
64 free_chain(c
->microsecond
);
70 static int component_compare(const void *_a
, const void *_b
) {
71 CalendarComponent
* const *a
= _a
, * const *b
= _b
;
73 if ((*a
)->start
< (*b
)->start
)
75 if ((*a
)->start
> (*b
)->start
)
78 if ((*a
)->stop
< (*b
)->stop
)
80 if ((*a
)->stop
> (*b
)->stop
)
83 if ((*a
)->repeat
< (*b
)->repeat
)
85 if ((*a
)->repeat
> (*b
)->repeat
)
91 static void normalize_chain(CalendarComponent
**c
) {
93 CalendarComponent
**b
, *i
, **j
, *next
;
97 for (i
= *c
; i
; i
= i
->next
) {
101 * While we're counting the chain, also normalize `stop`
102 * so the length of the range is a multiple of `repeat`
104 if (i
->stop
> i
->start
&& i
->repeat
> 0)
105 i
->stop
-= (i
->stop
- i
->start
) % i
->repeat
;
112 j
= b
= alloca(sizeof(CalendarComponent
*) * n
);
113 for (i
= *c
; i
; i
= i
->next
)
116 qsort(b
, n
, sizeof(CalendarComponent
*), component_compare
);
121 /* Drop non-unique entries */
122 for (k
= n
-1; k
> 0; k
--) {
123 if (component_compare(&b
[k
-1], &next
) == 0) {
135 static void fix_year(CalendarComponent
*c
) {
136 /* Turns 12 → 2012, 89 → 1989 */
139 if (c
->start
>= 0 && c
->start
< 70)
142 if (c
->stop
>= 0 && c
->stop
< 70)
145 if (c
->start
>= 70 && c
->start
< 100)
148 if (c
->stop
>= 70 && c
->stop
< 100)
155 int calendar_spec_normalize(CalendarSpec
*c
) {
158 if (c
->weekdays_bits
<= 0 || c
->weekdays_bits
>= BITS_WEEKDAYS
)
159 c
->weekdays_bits
= -1;
161 if (c
->end_of_month
&& !c
->day
)
162 c
->end_of_month
= false;
166 normalize_chain(&c
->year
);
167 normalize_chain(&c
->month
);
168 normalize_chain(&c
->day
);
169 normalize_chain(&c
->hour
);
170 normalize_chain(&c
->minute
);
171 normalize_chain(&c
->microsecond
);
176 _pure_
static bool chain_valid(CalendarComponent
*c
, int from
, int to
, bool end_of_month
) {
180 /* Forbid dates more than 28 days from the end of the month */
184 if (c
->start
< from
|| c
->start
> to
)
188 * c->repeat must be short enough so at least one repetition may
189 * occur before the end of the interval. For dates scheduled
190 * relative to the end of the month, c->start and c->stop
191 * correspond to the Nth last day of the month.
194 if (c
->stop
< from
|| c
->stop
> to
)
197 if (c
->start
+ c
->repeat
> c
->stop
)
200 if (end_of_month
&& c
->start
- c
->repeat
< from
)
203 if (!end_of_month
&& c
->start
+ c
->repeat
> to
)
208 return chain_valid(c
->next
, from
, to
, end_of_month
);
213 _pure_
bool calendar_spec_valid(CalendarSpec
*c
) {
216 if (c
->weekdays_bits
> BITS_WEEKDAYS
)
219 if (!chain_valid(c
->year
, MIN_YEAR
, MAX_YEAR
, false))
222 if (!chain_valid(c
->month
, 1, 12, false))
225 if (!chain_valid(c
->day
, 1, 31, c
->end_of_month
))
228 if (!chain_valid(c
->hour
, 0, 23, false))
231 if (!chain_valid(c
->minute
, 0, 59, false))
234 if (!chain_valid(c
->microsecond
, 0, 60*USEC_PER_SEC
-1, false))
240 static void format_weekdays(FILE *f
, const CalendarSpec
*c
) {
241 static const char *const days
[] = {
252 bool need_comma
= false;
256 assert(c
->weekdays_bits
> 0 && c
->weekdays_bits
<= BITS_WEEKDAYS
);
258 for (x
= 0, l
= -1; x
< (int) ELEMENTSOF(days
); x
++) {
260 if (c
->weekdays_bits
& (1 << x
)) {
264 fputc_unlocked(',', f
);
268 fputs_unlocked(days
[x
], f
);
275 fputs_unlocked(x
> l
+ 2 ? ".." : ",", f
);
276 fputs_unlocked(days
[x
-1], f
);
283 if (l
>= 0 && x
> l
+ 1) {
284 fputs_unlocked(x
> l
+ 2 ? ".." : ",", f
);
285 fputs_unlocked(days
[x
-1], f
);
289 static void format_chain(FILE *f
, int space
, const CalendarComponent
*c
, bool usec
) {
290 int d
= usec
? (int) USEC_PER_SEC
: 1;
295 fputc_unlocked('*', f
);
299 if (usec
&& c
->start
== 0 && c
->repeat
== USEC_PER_SEC
&& !c
->next
) {
300 fputc_unlocked('*', f
);
304 assert(c
->start
>= 0);
306 fprintf(f
, "%0*i", space
, c
->start
/ d
);
307 if (c
->start
% d
> 0)
308 fprintf(f
, ".%06i", c
->start
% d
);
311 fprintf(f
, "..%0*i", space
, c
->stop
/ d
);
313 fprintf(f
, ".%06i", c
->stop
% d
);
315 if (c
->repeat
> 0 && !(c
->stop
> 0 && c
->repeat
== d
))
316 fprintf(f
, "/%i", c
->repeat
/ d
);
317 if (c
->repeat
% d
> 0)
318 fprintf(f
, ".%06i", c
->repeat
% d
);
321 fputc_unlocked(',', f
);
322 format_chain(f
, space
, c
->next
, usec
);
326 int calendar_spec_to_string(const CalendarSpec
*c
, char **p
) {
335 f
= open_memstream(&buf
, &sz
);
339 if (c
->weekdays_bits
> 0 && c
->weekdays_bits
<= BITS_WEEKDAYS
) {
340 format_weekdays(f
, c
);
341 fputc_unlocked(' ', f
);
344 format_chain(f
, 4, c
->year
, false);
345 fputc_unlocked('-', f
);
346 format_chain(f
, 2, c
->month
, false);
347 fputc_unlocked(c
->end_of_month
? '~' : '-', f
);
348 format_chain(f
, 2, c
->day
, false);
349 fputc_unlocked(' ', f
);
350 format_chain(f
, 2, c
->hour
, false);
351 fputc_unlocked(':', f
);
352 format_chain(f
, 2, c
->minute
, false);
353 fputc_unlocked(':', f
);
354 format_chain(f
, 2, c
->microsecond
, true);
357 fputs_unlocked(" UTC", f
);
358 else if (c
->timezone
!= NULL
) {
359 fputc_unlocked(' ', f
);
360 fputs_unlocked(c
->timezone
, f
);
361 } else if (IN_SET(c
->dst
, 0, 1)) {
363 /* If daylight saving is explicitly on or off, let's show the used timezone. */
367 if (!isempty(tzname
[c
->dst
])) {
368 fputc_unlocked(' ', f
);
369 fputs_unlocked(tzname
[c
->dst
], f
);
373 r
= fflush_and_check(f
);
386 static int parse_weekdays(const char **p
, CalendarSpec
*c
) {
387 static const struct {
417 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
420 if (!startswith_no_case(*p
, day_nr
[i
].name
))
423 skip
= strlen(day_nr
[i
].name
);
425 if (!IN_SET((*p
)[skip
], 0, '-', '.', ',', ' '))
428 c
->weekdays_bits
|= 1 << day_nr
[i
].nr
;
433 if (l
> day_nr
[i
].nr
)
436 for (j
= l
+ 1; j
< day_nr
[i
].nr
; j
++)
437 c
->weekdays_bits
|= 1 << j
;
444 /* Couldn't find this prefix, so let's assume the
445 weekday was not specified and let's continue with
447 if (i
>= ELEMENTSOF(day_nr
))
448 return first
? 0 : -EINVAL
;
450 /* We reached the end of the string */
454 /* We reached the end of the weekday spec part */
456 *p
+= strspn(*p
, " ");
470 /* Support ranges with "-" for backwards compatibility */
471 } else if (**p
== '-') {
478 } else if (**p
== ',') {
483 /* Allow a trailing comma but not an open range */
484 if (IN_SET(**p
, 0, ' ')) {
485 *p
+= strspn(*p
, " ");
486 return l
< 0 ? 0 : -EINVAL
;
493 static int parse_one_number(const char *p
, const char **e
, unsigned long *ret
) {
498 value
= strtoul(p
, &ee
, 10);
509 static int parse_component_decimal(const char **p
, bool usec
, int *res
) {
511 const char *e
= NULL
;
517 r
= parse_one_number(*p
, &e
, &value
);
522 if (value
* USEC_PER_SEC
/ USEC_PER_SEC
!= value
)
525 value
*= USEC_PER_SEC
;
527 /* One "." is a decimal point, but ".." is a range separator */
528 if (e
[0] == '.' && e
[1] != '.') {
532 r
= parse_fractional_part_u(&e
, 6, &add
);
536 if (add
+ value
< value
)
551 static int const_chain(int value
, CalendarComponent
**c
) {
552 CalendarComponent
*cc
= NULL
;
556 cc
= new0(CalendarComponent
, 1);
570 static int calendarspec_from_time_t(CalendarSpec
*c
, time_t time
) {
572 CalendarComponent
*year
= NULL
, *month
= NULL
, *day
= NULL
, *hour
= NULL
, *minute
= NULL
, *us
= NULL
;
575 assert_se(gmtime_r(&time
, &tm
));
577 r
= const_chain(tm
.tm_year
+ 1900, &year
);
581 r
= const_chain(tm
.tm_mon
+ 1, &month
);
585 r
= const_chain(tm
.tm_mday
, &day
);
589 r
= const_chain(tm
.tm_hour
, &hour
);
593 r
= const_chain(tm
.tm_min
, &minute
);
597 r
= const_chain(tm
.tm_sec
* USEC_PER_SEC
, &us
);
611 static int prepend_component(const char **p
, bool usec
, CalendarComponent
**c
) {
612 int r
, start
, stop
= -1, repeat
= 0;
613 CalendarComponent
*cc
;
621 r
= parse_component_decimal(&e
, usec
, &start
);
625 if (e
[0] == '.' && e
[1] == '.') {
627 r
= parse_component_decimal(&e
, usec
, &stop
);
631 repeat
= usec
? USEC_PER_SEC
: 1;
636 r
= parse_component_decimal(&e
, usec
, &repeat
);
644 if (!IN_SET(*e
, 0, ' ', ',', '-', '~', ':'))
647 cc
= new0(CalendarComponent
, 1);
661 return prepend_component(p
, usec
, c
);
667 static int parse_chain(const char **p
, bool usec
, CalendarComponent
**c
) {
669 CalendarComponent
*cc
= NULL
;
679 r
= const_chain(0, c
);
682 (*c
)->repeat
= USEC_PER_SEC
;
690 r
= prepend_component(&t
, usec
, &cc
);
701 static int parse_date(const char **p
, CalendarSpec
*c
) {
704 CalendarComponent
*first
, *second
, *third
;
715 /* @TIMESTAMP — UNIX time in seconds since the epoch */
720 r
= parse_one_number(t
+ 1, &t
, &value
);
725 if ((unsigned long) time
!= value
)
728 r
= calendarspec_from_time_t(c
, time
);
733 return 1; /* finito, don't parse H:M:S after that */
736 r
= parse_chain(&t
, false, &first
);
740 /* Already the end? A ':' as separator? In that case this was a time, not a date */
741 if (IN_SET(*t
, 0, ':')) {
747 c
->end_of_month
= true;
748 else if (*t
!= '-') {
754 r
= parse_chain(&t
, false, &second
);
760 /* Got two parts, hence it's month and day */
761 if (IN_SET(*t
, 0, ' ')) {
762 *p
= t
+ strspn(t
, " ");
766 } else if (c
->end_of_month
) {
773 c
->end_of_month
= true;
774 else if (*t
!= '-') {
781 r
= parse_chain(&t
, false, &third
);
788 /* Got three parts, hence it is year, month and day */
789 if (IN_SET(*t
, 0, ' ')) {
790 *p
= t
+ strspn(t
, " ");
803 static int parse_calendar_time(const char **p
, CalendarSpec
*c
) {
804 CalendarComponent
*h
= NULL
, *m
= NULL
, *s
= NULL
;
814 /* If no time is specified at all, then this means 00:00:00 */
818 r
= parse_chain(&t
, false, &h
);
828 r
= parse_chain(&t
, false, &m
);
832 /* Already at the end? Then it's hours and minutes, and seconds are 0 */
842 r
= parse_chain(&t
, true, &s
);
846 /* At the end? Then it's hours, minutes and seconds */
854 r
= const_chain(0, &h
);
858 r
= const_chain(0, &m
);
863 r
= const_chain(0, &s
);
882 int calendar_spec_from_string(const char *p
, CalendarSpec
**spec
) {
890 c
= new0(CalendarSpec
, 1);
896 utc
= endswith_no_case(p
, " UTC");
899 p
= strndupa(p
, utc
- p
);
901 const char *e
= NULL
;
906 /* Check if the local timezone was specified? */
907 for (j
= 0; j
<= 1; j
++) {
908 if (isempty(tzname
[j
]))
911 e
= endswith_no_case(p
, tzname
[j
]);
922 /* Found one of the two timezones specified? */
923 if (IN_SET(j
, 0, 1)) {
924 p
= strndupa(p
, e
- p
- 1);
927 const char *last_space
;
929 last_space
= strrchr(p
, ' ');
930 if (last_space
!= NULL
&& timezone_is_valid(last_space
+ 1)) {
931 c
->timezone
= strdup(last_space
+ 1);
937 p
= strndupa(p
, last_space
- p
);
947 if (strcaseeq(p
, "minutely")) {
948 r
= const_chain(0, &c
->microsecond
);
952 } else if (strcaseeq(p
, "hourly")) {
953 r
= const_chain(0, &c
->minute
);
956 r
= const_chain(0, &c
->microsecond
);
960 } else if (strcaseeq(p
, "daily")) {
961 r
= const_chain(0, &c
->hour
);
964 r
= const_chain(0, &c
->minute
);
967 r
= const_chain(0, &c
->microsecond
);
971 } else if (strcaseeq(p
, "monthly")) {
972 r
= const_chain(1, &c
->day
);
975 r
= const_chain(0, &c
->hour
);
978 r
= const_chain(0, &c
->minute
);
981 r
= const_chain(0, &c
->microsecond
);
985 } else if (strcaseeq(p
, "annually") ||
986 strcaseeq(p
, "yearly") ||
987 strcaseeq(p
, "anually") /* backwards compatibility */ ) {
989 r
= const_chain(1, &c
->month
);
992 r
= const_chain(1, &c
->day
);
995 r
= const_chain(0, &c
->hour
);
998 r
= const_chain(0, &c
->minute
);
1001 r
= const_chain(0, &c
->microsecond
);
1005 } else if (strcaseeq(p
, "weekly")) {
1007 c
->weekdays_bits
= 1;
1009 r
= const_chain(0, &c
->hour
);
1012 r
= const_chain(0, &c
->minute
);
1015 r
= const_chain(0, &c
->microsecond
);
1019 } else if (strcaseeq(p
, "quarterly")) {
1021 r
= const_chain(1, &c
->month
);
1024 r
= const_chain(4, &c
->month
);
1027 r
= const_chain(7, &c
->month
);
1030 r
= const_chain(10, &c
->month
);
1033 r
= const_chain(1, &c
->day
);
1036 r
= const_chain(0, &c
->hour
);
1039 r
= const_chain(0, &c
->minute
);
1042 r
= const_chain(0, &c
->microsecond
);
1046 } else if (strcaseeq(p
, "biannually") ||
1047 strcaseeq(p
, "bi-annually") ||
1048 strcaseeq(p
, "semiannually") ||
1049 strcaseeq(p
, "semi-annually")) {
1051 r
= const_chain(1, &c
->month
);
1054 r
= const_chain(7, &c
->month
);
1057 r
= const_chain(1, &c
->day
);
1060 r
= const_chain(0, &c
->hour
);
1063 r
= const_chain(0, &c
->minute
);
1066 r
= const_chain(0, &c
->microsecond
);
1071 r
= parse_weekdays(&p
, c
);
1075 r
= parse_date(&p
, c
);
1080 r
= parse_calendar_time(&p
, c
);
1091 r
= calendar_spec_normalize(c
);
1095 if (!calendar_spec_valid(c
)) {
1104 calendar_spec_free(c
);
1108 static int find_end_of_month(struct tm
*tm
, bool utc
, int day
) {
1112 t
.tm_mday
= 1 - day
;
1114 if (mktime_or_timegm(&t
, utc
) < 0 ||
1115 t
.tm_mon
!= tm
->tm_mon
)
1121 static int find_matching_component(const CalendarSpec
*spec
, const CalendarComponent
*c
,
1122 struct tm
*tm
, int *val
) {
1123 const CalendarComponent
*p
= c
;
1124 int start
, stop
, d
= -1;
1137 if (spec
->end_of_month
&& p
== spec
->day
) {
1138 start
= find_end_of_month(tm
, spec
->utc
, start
);
1139 stop
= find_end_of_month(tm
, spec
->utc
, stop
);
1142 SWAP_TWO(start
, stop
);
1145 if (start
>= *val
) {
1147 if (!d_set
|| start
< d
) {
1152 } else if (c
->repeat
> 0) {
1155 k
= start
+ c
->repeat
* ((*val
- start
+ c
->repeat
- 1) / c
->repeat
);
1157 if ((!d_set
|| k
< d
) && (stop
< 0 || k
<= stop
)) {
1174 static bool tm_out_of_bounds(const struct tm
*tm
, bool utc
) {
1180 if (mktime_or_timegm(&t
, utc
) < 0)
1184 * Set an upper bound on the year so impossible dates like "*-02-31"
1185 * don't cause find_next() to loop forever. tm_year contains years
1186 * since 1900, so adjust it accordingly.
1188 if (tm
->tm_year
+ 1900 > MAX_YEAR
)
1191 /* Did any normalization take place? If so, it was out of bounds before */
1193 t
.tm_year
!= tm
->tm_year
||
1194 t
.tm_mon
!= tm
->tm_mon
||
1195 t
.tm_mday
!= tm
->tm_mday
||
1196 t
.tm_hour
!= tm
->tm_hour
||
1197 t
.tm_min
!= tm
->tm_min
||
1198 t
.tm_sec
!= tm
->tm_sec
;
1201 static bool matches_weekday(int weekdays_bits
, const struct tm
*tm
, bool utc
) {
1205 if (weekdays_bits
< 0 || weekdays_bits
>= BITS_WEEKDAYS
)
1209 if (mktime_or_timegm(&t
, utc
) < 0)
1212 k
= t
.tm_wday
== 0 ? 6 : t
.tm_wday
- 1;
1213 return (weekdays_bits
& (1 << k
));
1216 static int find_next(const CalendarSpec
*spec
, struct tm
*tm
, usec_t
*usec
) {
1228 /* Normalize the current date */
1229 (void) mktime_or_timegm(&c
, spec
->utc
);
1230 c
.tm_isdst
= spec
->dst
;
1233 r
= find_matching_component(spec
, spec
->year
, &c
, &c
.tm_year
);
1239 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1243 if (tm_out_of_bounds(&c
, spec
->utc
))
1247 r
= find_matching_component(spec
, spec
->month
, &c
, &c
.tm_mon
);
1252 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1254 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1258 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1262 r
= find_matching_component(spec
, spec
->day
, &c
, &c
.tm_mday
);
1264 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1265 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1268 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1272 if (!matches_weekday(spec
->weekdays_bits
, &c
, spec
->utc
)) {
1274 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1278 r
= find_matching_component(spec
, spec
->hour
, &c
, &c
.tm_hour
);
1280 c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1281 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1283 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1287 r
= find_matching_component(spec
, spec
->minute
, &c
, &c
.tm_min
);
1289 c
.tm_sec
= tm_usec
= 0;
1290 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1292 c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1296 c
.tm_sec
= c
.tm_sec
* USEC_PER_SEC
+ tm_usec
;
1297 r
= find_matching_component(spec
, spec
->microsecond
, &c
, &c
.tm_sec
);
1298 tm_usec
= c
.tm_sec
% USEC_PER_SEC
;
1299 c
.tm_sec
/= USEC_PER_SEC
;
1301 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1303 c
.tm_sec
= tm_usec
= 0;
1313 static int calendar_spec_next_usec_impl(const CalendarSpec
*spec
, usec_t usec
, usec_t
*next
) {
1322 if (usec
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
1326 t
= (time_t) (usec
/ USEC_PER_SEC
);
1327 assert_se(localtime_or_gmtime_r(&t
, &tm
, spec
->utc
));
1328 tm_usec
= usec
% USEC_PER_SEC
;
1330 r
= find_next(spec
, &tm
, &tm_usec
);
1334 t
= mktime_or_timegm(&tm
, spec
->utc
);
1338 *next
= (usec_t
) t
* USEC_PER_SEC
+ tm_usec
;
1342 typedef struct SpecNextResult
{
1347 int calendar_spec_next_usec(const CalendarSpec
*spec
, usec_t usec
, usec_t
*next
) {
1349 SpecNextResult
*shared
;
1353 if (isempty(spec
->timezone
))
1354 return calendar_spec_next_usec_impl(spec
, usec
, next
);
1356 shared
= mmap(NULL
, sizeof *shared
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
1357 if (shared
== MAP_FAILED
)
1358 return negative_errno();
1363 int fork_errno
= errno
;
1364 (void) munmap(shared
, sizeof *shared
);
1369 if (setenv("TZ", spec
->timezone
, 1) != 0) {
1370 shared
->return_value
= negative_errno();
1371 _exit(EXIT_FAILURE
);
1376 shared
->return_value
= calendar_spec_next_usec_impl(spec
, usec
, &shared
->next
);
1378 _exit(EXIT_SUCCESS
);
1381 r
= wait_for_terminate(pid
, NULL
);
1383 (void) munmap(shared
, sizeof *shared
);
1388 if (munmap(shared
, sizeof *shared
) != 0)
1389 return negative_errno();
1391 if (tmp
.return_value
== 0)
1394 return tmp
.return_value
;