1 ! Copyright (C) 2018 John Benediktsson
2 ! See http://factorcode.org/license.txt for BSD license
4 USING: accessors arrays ascii assocs calendar calendar.english
5 calendar.private combinators io kernel literals locals math
6 math.order math.parser math.ranges sequences splitting ;
10 :: parse-value ( value quot: ( value -- value' ) seq -- value )
12 { [ CHAR: , over member? ] [
13 "," split [ quot seq parse-value ] map concat ] }
14 { [ dup "*" = ] [ drop seq ] }
15 { [ CHAR: / over member? ] [
16 "/" split1 [ quot seq parse-value 0 over length 1 - ] dip
17 string>number <range> swap nths ] }
18 { [ CHAR: - over member? ] [
19 "-" split1 quot bi@ [a,b] ] }
21 } cond ; inline recursive
23 : parse-day ( str -- n )
24 dup string>number [ ] [
25 >lower $[ day-abbreviations3 [ >lower ] map ] index
28 : parse-month ( str -- n )
29 dup string>number [ ] [
30 >lower $[ month-abbreviations [ >lower ] map ] index
33 TUPLE: cronentry minutes hours days months days-of-week command ;
36 { "@yearly" "0 0 1 1 *" }
37 { "@annually" "0 0 1 1 *" }
38 { "@monthly" "0 0 1 * *" }
39 { "@weekly" "0 0 * * 0" }
40 { "@daily" "0 0 * * *" }
41 { "@midnight" "0 0 * * *" }
42 { "@hourly" "0 * * * *" }
45 : parse-cronentry ( entry -- cronentry )
46 " " split1 [ aliases ?at drop ] dip " " glue
47 " " split1 " " split1 " " split1 " " split1 " " split1 {
48 [ [ string>number ] T{ range f 0 60 1 } parse-value ]
49 [ [ string>number ] T{ range f 0 24 1 } parse-value ]
50 [ [ string>number ] T{ range f 1 31 1 } parse-value ]
51 [ [ parse-month ] T{ range f 1 12 1 } parse-value ]
52 [ [ parse-day ] T{ range f 0 7 1 } parse-value ]
54 } spread cronentry boa ;
58 :: (next-time-after) ( cronentry timestamp -- )
59 timestamp month>> :> month
60 cronentry months>> [ month >= ] find nip [
61 dup month = [ drop f ] [
62 timestamp 1 >>day 0 >>hour 0 >>minute month<< t
65 timestamp 1 >>day 0 >>hour 0 >>minute
66 cronentry months>> first >>month 1 +year
67 ] if* [ cronentry timestamp (next-time-after) ] when
69 timestamp day>> :> day
70 cronentry days>> [ day >= ] find nip [
71 dup day = [ drop f ] [
72 timestamp 0 >>hour 0 >>minute day<< t
75 timestamp 0 >>hour 0 >>minute
76 cronentry days>> first >>day 1 +month
77 ] if* [ cronentry timestamp (next-time-after) ] when
79 timestamp hour>> :> hour
80 cronentry hours>> [ hour >= ] find nip [
81 dup hour = [ drop f ] [
82 timestamp 0 >>minute hour<< t
86 cronentry hours>> first >>hour 1 +day
87 ] if* [ cronentry timestamp (next-time-after) ] when
89 timestamp minute>> :> minute
90 cronentry minutes>> [ minute >= ] find nip [
91 dup minute = [ drop f ] [ timestamp minute<< t ] if
93 timestamp cronentry minutes>> first >>minute 1 +hour
94 ] if* [ cronentry timestamp (next-time-after) ] when
96 timestamp day-of-week :> weekday
97 cronentry days-of-week>> [ weekday >= ] find nip [
98 cronentry days-of-week>> first 7 +
100 timestamp swap +day drop
101 cronentry timestamp (next-time-after)
106 : next-time-after ( cronentry timestamp -- timestamp )
107 1 minutes time+ 0 >>second [ (next-time-after) ] keep ;
109 : next-time ( cronentry -- timestamp )
110 now next-time-after ;
112 : next-times-after ( cronentry n timestamp -- timestamps )
113 swap [ dupd next-time-after dup ] replicate 2nip ;
115 : next-times ( cronentry n -- timestamps )
116 now next-times-after ;
118 : read-crontab ( -- entries )
119 lines harvest [ parse-cronentry ] map ;