icalPal 3.10.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/icalPal +3 -3
- data/ext/extconf.rb +9 -24
- data/lib/ToICalPal.rb +2 -2
- data/lib/event.rb +3 -3
- data/lib/options.rb +3 -9
- data/lib/rdt.rb +29 -11
- data/lib/reminder.rb +4 -11
- data/lib/version.rb +1 -1
- metadata +2 -3
- data/bin/icalpal +0 -363
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b48ee501e87dc1c4c597bc9e45b6fd977668f107f942c857186de23245d22a4d
|
|
4
|
+
data.tar.gz: a4010b5cd6e901fc3d4a6eaa850c4b3303c97e0ca1075d868598ed8ab428b243
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3f74dc669bd9b5352d12a5a40859cf1d2cab3cbbe3ed185223c5f8ec449af3115f415832191f6184ab76fe9ac74f0211ca80143d660440463f6a79080297ba54
|
|
7
|
+
data.tar.gz: afef75a278aa28007182a40ac9c1ed37369a5a72e0c3e8be92102160034b3f1ad013b879af0676e721cc8b0179de6c2ae28db023de657fb998fb73e53f06a54c
|
data/bin/icalPal
CHANGED
|
@@ -206,10 +206,10 @@ $rows.each do |row|
|
|
|
206
206
|
next if $opts[:ed] && item['completed'].zero?
|
|
207
207
|
next unless $opts[:id] || item['completed'].zero?
|
|
208
208
|
|
|
209
|
-
next if $opts[:dated] ==
|
|
210
|
-
next if $opts[:dated] ==
|
|
209
|
+
next if $opts[:dated] == 'undatedTasks' && item['due_date'] && item['due_date'].positive?
|
|
210
|
+
next if $opts[:dated] == 'datedTasks' && (!item['due_date'] || item['due_date'].zero?)
|
|
211
211
|
|
|
212
|
-
if $opts[:dated] ==
|
|
212
|
+
if $opts[:dated] == 'tasksDueBefore'
|
|
213
213
|
next unless item['due_date']
|
|
214
214
|
next unless item['due_date'].between?($opts[:from].to_i, $opts[:to].to_i)
|
|
215
215
|
end
|
data/ext/extconf.rb
CHANGED
|
@@ -7,33 +7,18 @@ begin
|
|
|
7
7
|
Gem.path.each { |p| gemdir = p if File.writable? p }
|
|
8
8
|
|
|
9
9
|
# Dependencies common to all environments
|
|
10
|
-
dependencies = %w[ plist
|
|
10
|
+
dependencies = %w[ plist ]
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
# All dependencies are included with the installation of Ruby in
|
|
13
|
+
# macOS. Adding these dependencies anyway will cause errors that
|
|
14
|
+
# prevent icalPal from being installed.
|
|
15
|
+
unless RUBY_VERSION >= '2.6'
|
|
16
|
+
dependencies.push('logger')
|
|
16
17
|
dependencies.push('csv')
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# requirements for sqlite3.
|
|
20
|
-
#
|
|
21
|
-
# macOS 15.5 (Sequoia) comes with version 1.3.13, so it does not
|
|
22
|
-
# need to be added as a dependency, and it cannot install anything
|
|
23
|
-
# newer:
|
|
24
|
-
#
|
|
25
|
-
# requires Ruby version >= 3.0, < 3.4.dev. The current ruby version is 2.6.10.
|
|
26
|
-
#
|
|
27
|
-
# Homebrew's Ruby formula does not come with sqlite3, so it does
|
|
28
|
-
# need to be added as a dependency, but it cannot install version
|
|
29
|
-
# 1.3.13:
|
|
30
|
-
#
|
|
31
|
-
# error: call to undeclared function
|
|
32
|
-
#
|
|
33
|
-
# So neither environment can install the other's sqlite3 gem. We
|
|
34
|
-
# must install sqlite3, but iff we are not building with macOS'
|
|
35
|
-
# Ruby installation.
|
|
18
|
+
dependencies.push('json')
|
|
19
|
+
dependencies.push('rdoc')
|
|
36
20
|
dependencies.push('sqlite3')
|
|
21
|
+
dependencies.push('yaml')
|
|
37
22
|
end
|
|
38
23
|
|
|
39
24
|
di = Gem::DependencyInstaller.new(install_dir: gemdir)
|
data/lib/ToICalPal.rb
CHANGED
|
@@ -97,7 +97,7 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
|
97
97
|
|
|
98
98
|
# Add a blank line
|
|
99
99
|
#
|
|
100
|
-
# @param
|
|
100
|
+
# @param _arg [Array] Ignored
|
|
101
101
|
def accept_blank_line(*_arg)
|
|
102
102
|
@res << "\n"
|
|
103
103
|
end
|
|
@@ -109,7 +109,7 @@ class RDoc::Markup::ToICalPal < RDoc::Markup::Formatter
|
|
|
109
109
|
# @option h [Integer] :level 2 for a property name
|
|
110
110
|
# @option h [String] :text The header's text
|
|
111
111
|
def accept_heading(h)
|
|
112
|
-
h
|
|
112
|
+
h = RDoc::Markup::Heading.new(h.level, colorize(@item['symbolic_color_name'], @item['color'], h.text)) if (h.level == 2) || COLOR_LABEL.any?(@prop)
|
|
113
113
|
@res << h.text
|
|
114
114
|
|
|
115
115
|
case h.level
|
data/lib/event.rb
CHANGED
|
@@ -95,8 +95,8 @@ module ICalPal
|
|
|
95
95
|
|
|
96
96
|
# Save as seconds, Time, RDT
|
|
97
97
|
ctime = obj[k] + ITIME
|
|
98
|
-
ctime -= Time.at(ctime).utc_offset if obj["#{k}_tz"] == '_float' && !zone
|
|
99
|
-
ttime = Time.at(ctime, in: zone)
|
|
98
|
+
ctime -= Time.at(ctime).utc_offset if obj["#{k.split('_')[0]}_tz"] == '_float' && !zone
|
|
99
|
+
ttime = (zone)? Time.at(ctime, in: zone) : Time.at(ctime)
|
|
100
100
|
|
|
101
101
|
@self["#{k[0]}seconds"] = ctime
|
|
102
102
|
@self["#{k[0]}ctime"] = ttime
|
|
@@ -265,7 +265,7 @@ module ICalPal
|
|
|
265
265
|
c['edate'] = ICalPal.nth(nth, day, nedate)
|
|
266
266
|
else
|
|
267
267
|
%w[ sdate edate ].each do |d|
|
|
268
|
-
diff = day - c[
|
|
268
|
+
diff = day - c[d].wday
|
|
269
269
|
diff += 7 if diff.negative?
|
|
270
270
|
|
|
271
271
|
t1 = Time.at(c[d].to_time)
|
data/lib/options.rb
CHANGED
|
@@ -275,15 +275,9 @@ module ICalPal
|
|
|
275
275
|
|
|
276
276
|
# Handle tasks command variants
|
|
277
277
|
if cli[:cmd] =~ /tasks/i
|
|
278
|
-
|
|
279
|
-
when 'undatedTasks'
|
|
280
|
-
cli[:dated] = 1
|
|
278
|
+
cli[:dated] = cli[:cmd]
|
|
281
279
|
|
|
282
|
-
|
|
283
|
-
cli[:dated] = 2
|
|
284
|
-
|
|
285
|
-
when 'tasksDueBefore'
|
|
286
|
-
cli[:dated] = 3
|
|
280
|
+
if cli[:dated] == 'tasksDueBefore'
|
|
287
281
|
cli.delete(:days) unless cli[:days]
|
|
288
282
|
cli[:from] = RDT.from_epoch(0) unless cli[:from]
|
|
289
283
|
cli[:to] = $today unless cli[:to]
|
|
@@ -310,7 +304,7 @@ module ICalPal
|
|
|
310
304
|
$log.level = opts[:debug]
|
|
311
305
|
|
|
312
306
|
# For posterity
|
|
313
|
-
opts[:ruby] =
|
|
307
|
+
opts[:ruby] = RUBY_DESCRIPTION
|
|
314
308
|
opts[:version] = @op.version
|
|
315
309
|
|
|
316
310
|
# From the Department of Redundancy Department
|
data/lib/rdt.rb
CHANGED
|
@@ -3,16 +3,28 @@ module ICalPal
|
|
|
3
3
|
class RDT < DateTime
|
|
4
4
|
|
|
5
5
|
# Create a new RDT from a Time object
|
|
6
|
+
#
|
|
7
|
+
# @param t [Time] The Time object
|
|
8
|
+
#
|
|
9
|
+
# @return [RDT] a new RDT
|
|
6
10
|
def self.from_time(t)
|
|
7
|
-
new(*t.to_a[0..5].reverse, (t.gmt_offset / 3600)
|
|
11
|
+
new(*t.to_a[0..5].reverse, Rational((t.gmt_offset / 3600), 24))
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
# Create a new RDT from seconds since epoch
|
|
15
|
+
#
|
|
16
|
+
# @param s [Integer] Seconds since the Unix epoch (Thu Jan 1 00:00:00 UTC 1970)
|
|
17
|
+
#
|
|
18
|
+
# @return [RDT] a new RDT
|
|
11
19
|
def self.from_epoch(s)
|
|
12
20
|
from_time(Time.at(s))
|
|
13
21
|
end
|
|
14
22
|
|
|
15
23
|
# Create a new RDT from seconds since iCal epoch
|
|
24
|
+
#
|
|
25
|
+
# @param s [Integer] Seconds since the iCal epoch (Jan 1 00:00:00 UTC 2001)
|
|
26
|
+
#
|
|
27
|
+
# @return [RDT] a new RDT
|
|
16
28
|
def self.from_itime(s)
|
|
17
29
|
from_epoch(s + ITIME)
|
|
18
30
|
end
|
|
@@ -25,15 +37,25 @@ module ICalPal
|
|
|
25
37
|
# @return [RDT] a new RDT
|
|
26
38
|
def self.conv(str)
|
|
27
39
|
case str
|
|
28
|
-
when 'yesterday' then $today
|
|
40
|
+
when 'yesterday' then $today.add(-1)
|
|
29
41
|
when 'today' then $today
|
|
30
|
-
when 'tomorrow' then $today
|
|
42
|
+
when 'tomorrow' then $today.add(1)
|
|
31
43
|
when /^\+([0-9]+)/ then $today + Regexp.last_match(1).to_i
|
|
32
44
|
when /^-([0-9]+)/ then $today - Regexp.last_match(1).to_i
|
|
33
45
|
else parse(str)
|
|
34
46
|
end
|
|
35
47
|
end
|
|
36
48
|
|
|
49
|
+
# Add a number of days accounting for daylight saving time changes
|
|
50
|
+
#
|
|
51
|
+
# @param days [Integer] Number of days to add
|
|
52
|
+
# @return [RDT] A new RDT
|
|
53
|
+
def add(days)
|
|
54
|
+
n = self + days
|
|
55
|
+
t = Time.parse("#{n.year}-#{n.month}-#{n.day} #{n.hour}:#{n.min}:#{n.sec}")
|
|
56
|
+
RDT.from_time(t)
|
|
57
|
+
end
|
|
58
|
+
|
|
37
59
|
# Values can be +day before yesterday+, +yesterday+,
|
|
38
60
|
# +today+, +tomorrow+, +day after tomorrow+, or the result from
|
|
39
61
|
# strftime
|
|
@@ -41,10 +63,10 @@ module ICalPal
|
|
|
41
63
|
# @return [String] A string representation of self relative to
|
|
42
64
|
# today.
|
|
43
65
|
def to_s
|
|
44
|
-
return strftime($opts[:df]) if $opts && $opts[:
|
|
66
|
+
return strftime($opts[:df]) if $opts && $opts[:df] && $opts[:nrd]
|
|
45
67
|
return super unless $today && $opts
|
|
46
68
|
|
|
47
|
-
case
|
|
69
|
+
case (self - $today).round
|
|
48
70
|
when -2 then 'day before yesterday'
|
|
49
71
|
when -1 then 'yesterday'
|
|
50
72
|
when 0 then 'today'
|
|
@@ -68,13 +90,13 @@ module ICalPal
|
|
|
68
90
|
to_time.to_i
|
|
69
91
|
end
|
|
70
92
|
|
|
71
|
-
# @param [Integer] Optional UTC offset
|
|
93
|
+
# @param z [Integer] Optional UTC offset
|
|
72
94
|
# @return [RDT] Self at 00:00:00
|
|
73
95
|
def day_start(z = zone)
|
|
74
96
|
RDT.new(year, month, day, 0, 0, 0, z)
|
|
75
97
|
end
|
|
76
98
|
|
|
77
|
-
# @param [Integer] Optional UTC offset
|
|
99
|
+
# @param z [Integer] Optional UTC offset
|
|
78
100
|
# @return [RDT] Self at 23:59:59
|
|
79
101
|
def day_end(z = zone)
|
|
80
102
|
RDT.new(year, month, day, 23, 59, 59, z)
|
|
@@ -90,9 +112,5 @@ module ICalPal
|
|
|
90
112
|
[ hour, min, sec ]
|
|
91
113
|
end
|
|
92
114
|
|
|
93
|
-
# @return [Array] Only the year, month and day of self
|
|
94
|
-
def ymd
|
|
95
|
-
[ year, month, day ]
|
|
96
|
-
end
|
|
97
115
|
end
|
|
98
116
|
end
|
data/lib/reminder.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
r 'tzinfo'
|
|
2
|
-
|
|
3
1
|
module ICalPal
|
|
4
2
|
# Class representing items from the <tt>Reminders</tt> database
|
|
5
3
|
class Reminder
|
|
@@ -103,14 +101,9 @@ module ICalPal
|
|
|
103
101
|
|
|
104
102
|
# Due date
|
|
105
103
|
if @self['due_date']
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
rescue TZInfo::InvalidTimezoneIdentifier
|
|
110
|
-
zone = '+00:00'
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
@self['due'] = RDT.from_time(Time.at(@self['due_date'], in: zone))
|
|
104
|
+
@self['due_date'] += ITIME
|
|
105
|
+
@self['due_date'] -= obj['utc_offset'] if obj['utc_offset']
|
|
106
|
+
@self['due'] = RDT.from_time(Time.at(@self['due_date']))
|
|
114
107
|
end
|
|
115
108
|
|
|
116
109
|
# Notes
|
|
@@ -196,9 +189,9 @@ r1.zNotes as notes,
|
|
|
196
189
|
r1.zPriority as priority,
|
|
197
190
|
r1.zContactHandles as messaging,
|
|
198
191
|
r1.zDueDateDeltaAlertsData as alert,
|
|
199
|
-
r1.zTimezone as timezone,
|
|
200
192
|
r1.zckIdentifier as id,
|
|
201
193
|
r1.zCompleted as completed,
|
|
194
|
+
r1.zDisplayDateUpdatedForSecondsFromGMT as utc_offset,
|
|
202
195
|
|
|
203
196
|
bl1.zBadgeEmblem as badge,
|
|
204
197
|
bl1.zColor as color,
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: icalPal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Rosen
|
|
@@ -22,7 +22,6 @@ extra_rdoc_files:
|
|
|
22
22
|
files:
|
|
23
23
|
- README.md
|
|
24
24
|
- bin/icalPal
|
|
25
|
-
- bin/icalpal
|
|
26
25
|
- ext/extconf.rb
|
|
27
26
|
- icalPal.gemspec
|
|
28
27
|
- lib/EventKit.rb
|
|
@@ -62,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
62
61
|
- !ruby/object:Gem::Version
|
|
63
62
|
version: '0'
|
|
64
63
|
requirements: []
|
|
65
|
-
rubygems_version:
|
|
64
|
+
rubygems_version: 4.0.3
|
|
66
65
|
specification_version: 4
|
|
67
66
|
summary: Command-line tool to query the macOS Calendar and Reminders
|
|
68
67
|
test_files: []
|
data/bin/icalpal
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
# rubocop: disable Style/RedundantBegin
|
|
4
|
-
|
|
5
|
-
# require a gem
|
|
6
|
-
#
|
|
7
|
-
# @param gem [String] The gem
|
|
8
|
-
def r(gem)
|
|
9
|
-
begin
|
|
10
|
-
# puts "require \"#{gem}\""
|
|
11
|
-
require gem
|
|
12
|
-
rescue LoadError => e
|
|
13
|
-
$stderr.puts "FATAL: icalPal is missing a dependency: #{gem}"
|
|
14
|
-
$stderr.puts e
|
|
15
|
-
$stderr.puts
|
|
16
|
-
abort "Try installing with 'gem install #{gem}'"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# require_relative a library
|
|
21
|
-
#
|
|
22
|
-
# @param library [String] The library
|
|
23
|
-
def rr(library)
|
|
24
|
-
begin
|
|
25
|
-
# puts "require_relative \"../lib/#{library}\""
|
|
26
|
-
require_relative "../lib/#{library}"
|
|
27
|
-
rescue LoadError => e
|
|
28
|
-
$stderr.puts "FATAL: Could not load library: #{library}"
|
|
29
|
-
$stderr.puts
|
|
30
|
-
abort e.message
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# rubocop: enable Style/RedundantBegin
|
|
35
|
-
|
|
36
|
-
%w[ logger csv json rdoc sqlite3 yaml ].each { |g| r g }
|
|
37
|
-
%w[ icalPal defaults options utils ].each { |l| rr l }
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
##################################################
|
|
41
|
-
# Load options
|
|
42
|
-
|
|
43
|
-
# All kids love log!
|
|
44
|
-
$log = Logger.new(STDERR, { level: $defaults[:common][:debug] })
|
|
45
|
-
$log.formatter = proc do |s, t, _p, m| # Severity, time, progname, msg
|
|
46
|
-
format("[%-5<sev>s] %<time>s [%<file>s:%<line>5s] - %<message>s\n",
|
|
47
|
-
{
|
|
48
|
-
sev: s,
|
|
49
|
-
time: t.strftime('%H:%M:%S.%L'),
|
|
50
|
-
file: caller(4, 1)[0].split('/')[-1].split(':')[0],
|
|
51
|
-
line: caller(4, 1)[0].split('/')[-1].split(':')[1],
|
|
52
|
-
message: m
|
|
53
|
-
})
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
$opts = ICalPal::Options.new.parse_options
|
|
57
|
-
|
|
58
|
-
$rows = [] # Rows from the database
|
|
59
|
-
$sections = [] # Calendar list sections
|
|
60
|
-
$items = [] # Items to be printed
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
##################################################
|
|
64
|
-
# All kids love log!
|
|
65
|
-
|
|
66
|
-
$log.info("Options:\n#{$opts.to_json}")
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
##################################################
|
|
70
|
-
# Add an item to the list
|
|
71
|
-
#
|
|
72
|
-
# @param item[Object]
|
|
73
|
-
|
|
74
|
-
def add(item)
|
|
75
|
-
$log.debug("Adding #{item.dump} #{item['UUID']} (#{item['title']})") if item['UUID']
|
|
76
|
-
|
|
77
|
-
$items.push(item)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
##################################################
|
|
82
|
-
# Load the data
|
|
83
|
-
|
|
84
|
-
# What are we getting?
|
|
85
|
-
klass = ICalPal.call($opts[:cmd])
|
|
86
|
-
success = false
|
|
87
|
-
|
|
88
|
-
# Get it
|
|
89
|
-
$opts[:db].each do |db|
|
|
90
|
-
$log.debug("Trying #{db}")
|
|
91
|
-
|
|
92
|
-
if klass == ICalPal::Reminder
|
|
93
|
-
# Load all .sqlite files
|
|
94
|
-
$log.debug("Loading *.sqlite in #{db}")
|
|
95
|
-
Dir.glob("#{db}/*.sqlite").each do |d|
|
|
96
|
-
success = true
|
|
97
|
-
|
|
98
|
-
rows = klass.load_data(d, klass::QUERY)
|
|
99
|
-
$rows += rows
|
|
100
|
-
|
|
101
|
-
sections = klass.load_data(d, klass::SECTIONS_QUERY)
|
|
102
|
-
$sections += sections
|
|
103
|
-
|
|
104
|
-
$log.info("Loaded #{rows.length} rows and #{sections.length} sections from #{d}")
|
|
105
|
-
end
|
|
106
|
-
else
|
|
107
|
-
# Load database
|
|
108
|
-
rows = ICalPal.load_data(db, klass::QUERY)
|
|
109
|
-
$rows += rows
|
|
110
|
-
|
|
111
|
-
success = true
|
|
112
|
-
|
|
113
|
-
$log.info("Loaded #{rows.length} rows from #{db}")
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
rescue Errno::EPERM
|
|
117
|
-
# Probably need Full Disk Access
|
|
118
|
-
|
|
119
|
-
rescue SQLite3::CantOpenException
|
|
120
|
-
# Non-fatal exception, try the next one
|
|
121
|
-
|
|
122
|
-
rescue StandardError => e
|
|
123
|
-
# Log the error and (try to) continue
|
|
124
|
-
$log.error("#{db}: #{e.message}")
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Make sure we opened at least one database
|
|
128
|
-
unless success
|
|
129
|
-
$log.fatal('Could not open database')
|
|
130
|
-
|
|
131
|
-
# SQLite3 does not return useful error messages. If any databases
|
|
132
|
-
# failed because of EPERM (operation not permitted), our parent
|
|
133
|
-
# process might need Full Disk Access, and we should suggest that.
|
|
134
|
-
eperm = 0
|
|
135
|
-
|
|
136
|
-
$opts[:db].each do |db|
|
|
137
|
-
# Use a real open to get a useful error
|
|
138
|
-
File.open(db).close
|
|
139
|
-
rescue Exception => e
|
|
140
|
-
$log.fatal("#{e.class}: #{db}")
|
|
141
|
-
|
|
142
|
-
eperm = 1 if e.instance_of?(Errno::EPERM)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
if eperm.positive?
|
|
146
|
-
$stderr.puts
|
|
147
|
-
$stderr.puts "Does #{ancestor} have Full Disk Access in System Settings?"
|
|
148
|
-
$stderr.puts
|
|
149
|
-
$stderr.puts "Try running: open 'x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles'"
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
abort
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
$log.debug("Loaded #{$rows.length} #{klass} items")
|
|
156
|
-
$log.info("Window is #{$opts[:from]} to #{$opts[:to]}") if $opts[:from]
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
##################################################
|
|
160
|
-
# Process the data
|
|
161
|
-
|
|
162
|
-
# Add rows
|
|
163
|
-
$rows.each do |row|
|
|
164
|
-
# --es/--is
|
|
165
|
-
next if $opts[:es].any? row['account']
|
|
166
|
-
next unless $opts[:is].empty? || ($opts[:is].any? row['account'])
|
|
167
|
-
|
|
168
|
-
# --ec/--ic
|
|
169
|
-
unless klass == ICalPal::Store || !row['calendar']
|
|
170
|
-
next if $opts[:ec].any? row['calendar']
|
|
171
|
-
next unless $opts[:ic].empty? || ($opts[:ic].any? row['calendar'])
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
# Instantiate an item
|
|
175
|
-
item = klass.new(row)
|
|
176
|
-
|
|
177
|
-
# --et/--it
|
|
178
|
-
next if $opts[:et].any? item['type']
|
|
179
|
-
next unless $opts[:it].empty? || ($opts[:it].any? item['type'])
|
|
180
|
-
|
|
181
|
-
# --el/--il
|
|
182
|
-
next if $opts[:el].any? item['list_name']
|
|
183
|
-
next unless $opts[:il].empty? || ($opts[:il].any? item['list_name'])
|
|
184
|
-
|
|
185
|
-
# --match
|
|
186
|
-
if $opts[:match]
|
|
187
|
-
r = $opts[:match].split('=')
|
|
188
|
-
|
|
189
|
-
if item[r[0]].to_s.respond_to?(:match)
|
|
190
|
-
next unless item[r[0]].to_s =~ Regexp.new(r[1], Regexp::IGNORECASE)
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
if item.is_a?(ICalPal::Event)
|
|
195
|
-
# Check for all-day and cancelled events
|
|
196
|
-
next if $opts[:ea] && item['all_day'].positive?
|
|
197
|
-
next if $opts[:ia] && !item['all_day'].positive?
|
|
198
|
-
next if item['status'] == :canceled
|
|
199
|
-
|
|
200
|
-
(item['has_recurrences'].positive?)?
|
|
201
|
-
item.recurring.each { |j| add(j) } :
|
|
202
|
-
item.non_recurring.each { |j| add(j) }
|
|
203
|
-
else
|
|
204
|
-
# Check for completed or dated reminders
|
|
205
|
-
if item.is_a?(ICalPal::Reminder)
|
|
206
|
-
next if $opts[:ed] && item['completed'].zero?
|
|
207
|
-
next unless $opts[:id] || item['completed'].zero?
|
|
208
|
-
|
|
209
|
-
next if $opts[:dated] == 1 && item['due_date'] && item['due_date'].positive?
|
|
210
|
-
next if $opts[:dated] == 2 && (!item['due_date'] || item['due_date'].zero?)
|
|
211
|
-
|
|
212
|
-
if $opts[:dated] == 3
|
|
213
|
-
next unless item['due_date']
|
|
214
|
-
next unless item['due_date'].between?($opts[:from].to_i, $opts[:to].to_i)
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
add(item)
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# Add placeholders for empty days
|
|
223
|
-
if $opts[:sed] && $opts[:sd] && klass == ICalPal::Event
|
|
224
|
-
days = $items.collect { |i| i['sday'] }.uniq.sort
|
|
225
|
-
|
|
226
|
-
$opts[:days].times do |n|
|
|
227
|
-
day = $opts[:from] + n
|
|
228
|
-
$items.push(klass.new(day)) unless days.any? { |i| i.to_s == day.to_s }
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
# Sort the rows
|
|
233
|
-
begin
|
|
234
|
-
$sort_attrs = []
|
|
235
|
-
$sort_attrs.push $opts[:sep] if $opts[:sep]
|
|
236
|
-
$sort_attrs.push $opts[:sort] if $opts[:sort]
|
|
237
|
-
$sort_attrs.push 'sdate'
|
|
238
|
-
|
|
239
|
-
$log.info("Sorting #{$items.count} items by #{$sort_attrs}, reverse #{$opts[:reverse].inspect}")
|
|
240
|
-
|
|
241
|
-
$items.sort!
|
|
242
|
-
$items.reverse! if $opts[:reverse]
|
|
243
|
-
rescue Exception => e
|
|
244
|
-
$log.info("Sorting failed: #{e}\n")
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
$log.debug("#{$items.count} items remain")
|
|
248
|
-
|
|
249
|
-
# Configure formatting
|
|
250
|
-
mu = case $opts[:output]
|
|
251
|
-
when 'ansi' then RDoc::Markup::ToAnsi.new
|
|
252
|
-
when 'default' then RDoc::Markup::ToICalPal.new($opts)
|
|
253
|
-
when 'html'
|
|
254
|
-
rdoc = RDoc::Options.new
|
|
255
|
-
rdoc.pipe = true
|
|
256
|
-
rdoc.output_decoration = false
|
|
257
|
-
RDoc::Markup::ToHtml.new(rdoc)
|
|
258
|
-
when 'md' then RDoc::Markup::ToMarkdown.new
|
|
259
|
-
when 'rdoc' then RDoc::Markup::ToRdoc.new
|
|
260
|
-
when 'toc' then RDoc::Markup::ToTableOfContents.new
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
##################################################
|
|
265
|
-
# Print the data
|
|
266
|
-
|
|
267
|
-
items = $items[0..($opts[:li] - 1)]
|
|
268
|
-
|
|
269
|
-
unless mu
|
|
270
|
-
$log.debug("Output in #{$opts[:output]} format")
|
|
271
|
-
|
|
272
|
-
puts case $opts[:output]
|
|
273
|
-
when 'csv'
|
|
274
|
-
# Get all headers
|
|
275
|
-
headers = []
|
|
276
|
-
items.each { |i| headers += i.keys }
|
|
277
|
-
headers.uniq!
|
|
278
|
-
|
|
279
|
-
# Populate a CSV::Table
|
|
280
|
-
table = CSV::Table.new([], headers: headers)
|
|
281
|
-
items.each { |i| table << i.to_csv(headers) }
|
|
282
|
-
|
|
283
|
-
table
|
|
284
|
-
when 'hash' then items.map { |i| i.self }
|
|
285
|
-
when 'json' then items.map { |i| i.self }.to_json
|
|
286
|
-
when 'xml'
|
|
287
|
-
xml = items.map { |i| "<#{$opts[:cmd].chomp('s')}>#{i.to_xml}</#{$opts[:cmd].chomp('s')}>" }
|
|
288
|
-
"<#{$opts[:cmd]}>\n#{xml.join}</#{$opts[:cmd]}>"
|
|
289
|
-
when 'yaml' then items.map { |i| i.self }.to_yaml
|
|
290
|
-
when 'remind' then items.map { |i|
|
|
291
|
-
"REM #{i['sdate'].strftime('%F AT %R')} " +
|
|
292
|
-
"DURATION #{((i['edate'] - i['sdate']).to_f * 1440).to_i} " +
|
|
293
|
-
"MSG #{i['title'].gsub(/([[:cntrl:]])/) { |c| c.dump[1..-2] }}"
|
|
294
|
-
}.join("\n")
|
|
295
|
-
else abort "No formatter for #{$opts[:output]}"
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
exit
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
$log.debug("Formatting with #{mu.inspect}")
|
|
302
|
-
|
|
303
|
-
doc = RDoc::Markup::Document.new
|
|
304
|
-
section = nil
|
|
305
|
-
|
|
306
|
-
items.each_with_index do |i, j|
|
|
307
|
-
$log.debug("Print #{j}: #{i.inspect}")
|
|
308
|
-
|
|
309
|
-
# --li
|
|
310
|
-
break if $opts[:li].positive? && j >= $opts[:li]
|
|
311
|
-
|
|
312
|
-
# Use RDoc::Markup::Verbatim to save the item
|
|
313
|
-
v = RDoc::Markup::Verbatim.new
|
|
314
|
-
v.format = i
|
|
315
|
-
doc << v
|
|
316
|
-
|
|
317
|
-
# Sections
|
|
318
|
-
if $opts[:sep] && section != i[$opts[:sep]]
|
|
319
|
-
$log.debug("New section '#{$opts[:sep]}': #{i[$opts[:sep]]}")
|
|
320
|
-
|
|
321
|
-
doc << RDoc::Markup::Raw.new($opts[:sep])
|
|
322
|
-
|
|
323
|
-
doc << RDoc::Markup::BlankLine.new if j.positive?
|
|
324
|
-
if i[$opts[:sep]]
|
|
325
|
-
doc << RDoc::Markup::Heading.new(1, i[$opts[:sep]].to_s)
|
|
326
|
-
doc << RDoc::Markup::Rule.new(0)
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
section = i[$opts[:sep]]
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
# Item
|
|
333
|
-
props = RDoc::Markup::List.new(:BULLET)
|
|
334
|
-
|
|
335
|
-
# Properties
|
|
336
|
-
$opts[:props].each_with_index do |prop, k|
|
|
337
|
-
value = i[prop]
|
|
338
|
-
|
|
339
|
-
next unless value
|
|
340
|
-
next if value.is_a?(Array) && !value[0]
|
|
341
|
-
next if value.is_a?(String) && value.empty?
|
|
342
|
-
|
|
343
|
-
$log.debug("#{prop}: #{value}")
|
|
344
|
-
|
|
345
|
-
# Use Raw to save the property
|
|
346
|
-
props << RDoc::Markup::Raw.new(prop)
|
|
347
|
-
|
|
348
|
-
if k.positive?
|
|
349
|
-
props << RDoc::Markup::BlankLine.new unless (i['placeholder'] || $opts[:ps])
|
|
350
|
-
props << RDoc::Markup::ListItem.new(prop, RDoc::Markup::Paragraph.new(value)) unless (i['placeholder'])
|
|
351
|
-
else
|
|
352
|
-
# First property, value only
|
|
353
|
-
props << RDoc::Markup::Heading.new(2, value.to_s)
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
# Print it
|
|
358
|
-
props << RDoc::Markup::BlankLine.new unless props.empty?
|
|
359
|
-
|
|
360
|
-
doc << props
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
print doc.accept(mu)
|