blob: ac5caecd7e6b81af064453854b053b93fe18b0ca (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
#!/bin/zsh
# All arguments are joined with spaces and inserted into the calendar
# file at the appropriate point.
#
# While the function compares the date of the new entry with dates in the
# existing calendar file, it does not do any sorting; it inserts the new
# entry before the first existing entry with a later date and time.
emulate -L zsh
setopt extendedglob
local context=":datetime:calendar_add:"
local calendar newfile REPLY lastline opt
local -a calendar_entries lockfiles reply
integer my_date done rstat nolock nobackup new_recurring old_recurring
local -A reply parse_new parse_old recurring_uids
autoload -U calendar_{parse,read,lockfiles}
while getopts "BL" opt; do
case $opt in
(B)
nobackup=1
;;
(L)
nolock=1
;;
(*)
return 1
;;
esac
done
shift $(( OPTIND - 1 ))
# Read the calendar file from the calendar-file style
zstyle -s $context calendar-file calendar ||
calendar=~/calendar
newfile=$calendar.new.$HOST.$$
local addline="$*"
if ! calendar_parse $addline; then
print "$0: failed to parse date/time" >&2
return 1
fi
parse_new=("${(@kv)reply}")
(( my_date = $parse_new[time] ))
[[ -n $parse_new[rpttime] ]] && (( new_recurring = 1 ))
if zstyle -t $context reformat-date; then
local datefmt
zstyle -s $context date-format datefmt ||
datefmt="%a %b %d %H:%M:%S %Z %Y"
strftime -s REPLY $datefmt $parse_new[time]
addline="$REPLY $parse_new[text1]"
fi
# $calendar doesn't necessarily exist yet.
local -a match mbegin mend
local my_uid their_uid
# Match a UID, a unique identifier for the entry inherited from
# text/calendar format.
local uidpat='(|*[[:space:]])UID[[:space:]]##(#b)([[:xdigit:]]##)(|[[:space:]]*)'
if [[ $addline = ${~uidpat} ]]; then
my_uid=${(U)match[1]}
fi
# start of subshell for OS file locking
(
# start of block for following always to clear up lockfiles.
# Not needed but harmless if OS file locking is used.
{
if (( ! nolock )); then
if zmodload -F zsh/system b:zsystem && zsystem supports flock; then
zsystem flock $calendar
else
calendar_lockfiles $calendar || exit 1
fi
fi
if [[ -f $calendar ]]; then
calendar_read $calendar
if [[ -n $my_uid ]]; then
# Pre-scan to find recurring events with a UID
for line in $calendar_entries; do
calendar_parse $line || continue
# Recurring with a UID?
if [[ -n $reply[rpttime] && $line = ${~uidpat} ]]; then
# Yes, so record this as a recurring event.
their_uid=${(U)match[1]}
recurring_uids[$their_uid]=$reply[time]
fi
done
fi
{
for line in $calendar_entries; do
calendar_parse $line || continue
parse_old=("${(@kv)reply}")
if (( ! done && ${parse_old[time]} > my_date )); then
print -r -- $addline
(( done = 1 ))
fi
if [[ -n $parse_old[rpttime] ]]; then
(( old_recurring = 1 ))
else
(( old_recurring = 0 ))
fi
if [[ -n $my_uid && $line = ${~uidpat} ]]; then
their_uid=${(U)match[1]}
if [[ $my_uid = $their_uid ]]; then
# Deal with recurrences, being careful in case there
# are one-off variants that don't replace recurrences.
#
# Bug 1: "calendar" still doesn't know about one-off variants.
# Bug 2: neither do I; how do we know which occurrence
# it replaces?
# Bug 3: the code for calculating recurrences is awful anyway.
if (( old_recurring && new_recurring )); then
# Replacing a recurrence; there can be only one.
continue
elif (( ! new_recurring )); then
# Not recurring. See if we have previously found
# a recurrent version
[[ -n $recurring_uids[$their_uid] ]] && (( old_recurring = 1 ))
# No, so assume this is a straightforward replacement
# of a non-recurring event.
(( ! old_recurring )) && continue
# It's recurring, but if this is a one-off at the
# same time as the previous one, replace anyway.
[[ -z $parse_old[$rpttime] ]] &&
(( ${parse_new[time]} == ${parse_old[time]} )) &&
continue
fi
fi
fi
if [[ $parse_old[time] -eq $my_date && $line = $addline ]]; then
(( done )) && continue # paranoia: shouldn't happen
(( done = 1 ))
fi
print -r -- $line
done
(( done )) || print -r -- $addline
} >$newfile
if (( ! nobackup )); then
if ! mv $calendar $calendar.old; then
print "Couldn't back up $calendar to $calendar.old.
New calendar left in $newfile." >&2
(( rstat = 1 ))
fi
fi
else
print -r -- $line >$newfile
fi
if (( !rstat )) && ! mv $newfile $calendar; then
print "Failed to rename $newfile to $calendar.
Old calendar left in $calendar.old." >&2
(( rstat = 1 ))
fi
} always {
(( ${#lockfiles} )) && rm -f $lockfiles
}
exit $rstat
)
|