filters: posix compliant rewrite of calendar filter

Rewrite of the awk calendar filter to make it posix compliant. Tested
with awk --posix (awk -V = GNU Awk 5.1.1). Also added some improvements
to readability and formatting.

This complements commit 3ef4a3ca05 ("filters: try and make awk scripts
posix compliant").

Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
Koni Marti 2022-07-24 01:25:24 +02:00 committed by Robin Jarry
parent fb5558da81
commit ab941ebc6a
1 changed files with 115 additions and 184 deletions

View File

@ -9,38 +9,48 @@
BEGIN { BEGIN {
UIDS[0]; UIDS[0];
people_attending[0]; people_attending[0];
people_partstat[0]; people_partstat[0];
people_rsvp[0]; people_rsvp[0];
# use a colon to separate the type of data line from the actual contents # use a colon to separate the type of data line from the actual contents
FS = ":"; FS = ":";
}
method = "" {
prodid = "" # remove carriage return from every line
gsub(/\r/, "")
first = 0
} }
/^[ ]/ { /^[ ]/ {
# this block deals with the continuation lines that start with a whitespace
#
line = $0
# remove trailing whitespaces
gsub(/^[ ]/, "", line)
# assumes continuation lines start with a space # assumes continuation lines start with a space
if (indescription) { if (indescription) {
entry = entry gensub("\r", "", "g", gensub("^[ ]", "", 1, $0)); entry = entry line
} else if (insummary) { } else if (insummary) {
summary = summary gensub("\r", "", "g", gensub("^[ ]", "", 1, $0)) summary = summary line
} else if (inattendee) { } else if (inattendee) {
attendee = attendee gensub("\r", "", "g", gensub("^[ ]", "", 1, $0)) attendee = attendee line
} else if (inorganizer) { } else if (inorganizer) {
organizer = organizer gensub("\r", "", "g", gensub("^[ ]", "", 1, $0)) organizer = organizer line
} else if (inlocation) { } else if (inlocation) {
location = location unescape(gensub("\r", "", "g", $0), 0); location = location unescape(line, 0)
} }
} }
/^BEGIN:VALARM/,/^END:VALARM/ {
next
}
/^BEGIN:VEVENT/ { /^BEGIN:VEVENT/ {
# start of an event: initialize global values used for each event # start of an event: initialize global values used for each event
date = ""; start_date = "";
end_date = "";
entry = "" entry = ""
id = "" id = ""
@ -49,7 +59,6 @@ BEGIN {
inattendee = 0 inattendee = 0
inorganizer = 0 inorganizer = 0
inlocation = 0 inlocation = 0
in_alarm = 0
location = "" location = ""
status = "" status = ""
@ -60,27 +69,11 @@ BEGIN {
rrend = "" rrend = ""
rcount = "" rcount = ""
intfreq = "" intfreq = ""
idx = 0
delete people_attending; delete people_attending;
delete people_partstat; delete people_partstat;
delete people_rsvp; delete people_rsvp;
if (first == 0) {
first = 1
if (method != "")
print " METHOD " method
if (prodid != "")
print " PRODID " prodid
print ""
}
}
/^BEGIN:VALARM/ {
in_alarm = 1
}
/^END:VALARM/ {
in_alarm = 0
} }
/^[A-Z]/ { /^[A-Z]/ {
@ -97,130 +90,99 @@ BEGIN {
inlocation = 0; inlocation = 0;
} }
/^DTSTART;VALUE=DATE/ { /^DTSTART[:;]/ {
date = datestring($2); tz = get_value($0, "TZID=[^:;]*", "=")
start_date = datetimestring($2, tz);
} }
/^DTEND;VALUE=DATE/ { /^DTEND[:;]/ {
end_date = datestring($2); tz = get_value($0, "TZID=[^:;]*", "=")
}
/^DTSTART[:;][^V]/ {
tz = "";
match($0, /TZID=([^:]*)/, a)
{
tz = a[1];
}
date = datetimestring($2, tz);
}
/^DTEND[:;][^V]/ {
tz = "";
match($0, /TZID=([^:]*)/, a)
{
tz = a[1];
}
end_date = datetimestring($2, tz); end_date = datetimestring($2, tz);
} }
/^RRULE:FREQ=(DAILY|WEEKLY|MONTHLY|YEARLY)/ { /^RRULE[:]/ {
# TODO: handle BYDAY values for events that repeat weekly for multiple days freq = get_value($0, "FREQ=[^:;]*", "=")
# (e.g. a "Gym" event) interval = get_value($0, "INTERVAL=[^:;]*", "=")
rrend = get_value($0, "UNTIL=[^:;]*", "=")
# get the d, w, m or y value rcount = get_value($0, "COUNT=[^:;]*", "=")
freq = tolower(gensub(/.*FREQ=(.).*/, "\\1", 1, $0)) intfreq = tolower(freq)
# get the interval, and use 1 if none specified if (interval != "")
interval = $0 ~ /INTERVAL=/ ? gensub(/.*INTERVAL=([0-9]+).*/, "\\1", 1, $0) : 1 intfreq = " +" interval intfreq
# get the enddate of the rule and use "" if none specified
rrend = $0 ~ /UNTIL=/ ? datestring(gensub(/.*UNTIL=([0-9]{8}).*/, "\\1", 1, $0)) : ""
rcount = $0 ~ /COUNT=/ ? gensub(/.*COUNT=([0-9]+).*/, "\\1", 1, $0) : ""
# build the repetitor value
intfreq = " +" interval freq
}
/^DESCRIPTION/ {
if (!in_alarm) {
entry = entry gensub("\r", "", "g", gensub($1":", "", 1, $0));
indescription = 1;
}
}
/^SUMMARY/ {
if (!in_alarm) {
summary = gensub("\r", "", "g", gensub($1":", "", 1, $0));
insummary = 1;
}
}
/^UID/ {
if (!in_alarm) {
id = gensub("\r", "", "g", $2);
}
} }
/^METHOD/ { /^METHOD/ {
method = gensub("\r", "", "g", $2); method = $2
} }
/^PRODID/ { /^UID/ {
prodid = gensub("\r", "", "g", $2); id = $2
}
/^STATUS/ {
status = $2
}
/^DESCRIPTION/ {
entry = entry $2
indescription = 1;
}
/^SUMMARY/ {
summary = $2
insummary = 1;
} }
/^ORGANIZER/ { /^ORGANIZER/ {
organizer = gensub("\r", "", "g", $0); organizer = $0
inorganizer = 1; inorganizer = 1;
} }
/^LOCATION/ { /^LOCATION/ {
location = unescape(gensub("\r", "", "g", $2), 0); location = unescape($2, 0);
inlocation = 1; inlocation = 1;
} }
/^STATUS/ {
status = gensub("\r", "", "g", $2);
}
/^ATTENDEE/ { /^ATTENDEE/ {
attendee = gensub("\r", "", "g", $0); attendee = $0
inattendee = 1; inattendee = 1;
} }
/^END:VEVENT/ { /^END:VEVENT/ {
#output event #output event
if (method != "") {
printf "\n This is a meeting %s\n\n", method
}
fmt = " %-14s%s\n"
is_duplicate = (id in UIDS); is_duplicate = (id in UIDS);
if(is_duplicate == 0) { if(is_duplicate == 0) {
print "* SUMMARY " gensub("^[ ]+", "", "g", unescape(summary, 0)) printf fmt, "SUMMARY", unescape(summary, 0)
if(length(location)) if(location != "")
print " LOCATION " location printf fmt, "LOCATION", location
if(organizer != "") if(organizer != "")
print " ORGANIZER " organizer printf fmt, "ORGANIZER", organizer
for (cn in people_attending) { for (idx in people_attending) {
print " ATTENDEE " cn printf fmt, "ATTENDEE [" idx "]", people_attending[idx]
partstat = people_partstat[cn] partstat = people_partstat[idx]
if (partstat != "") { if (partstat != "") {
print " STATUS " partstat printf fmt, "", "STATUS\t" partstat
} }
rsvp = people_rsvp[cn] rsvp = people_rsvp[idx]
if (rsvp != "") { if (rsvp != "") {
print " RSVP " rsvp printf fmt, "", "RSVP\t" rsvp
} }
} }
print " START " date printf fmt, "START", start_date
print " END " end_date printf fmt, "END", end_date
if (intfreq != "") { if (intfreq != "") {
print "" printf "\n"fmt, "RECURRENCE", intfreq
print " RECURRENCE " intfreq
if (rcount != "") if (rcount != "")
print " COUNTS " rcount printf fmt, "COUNTS", rcount
if (rrend != "") if (rrend != "")
print " END DATE " rrend printf fmt, "END DATE", rrend
} }
if(entry != "")
print "" print "\n" unescape(entry, 1);
if(length(entry)>1)
print gensub("^[ ]+", "", "g", unescape(entry, 1));
UIDS[id] = 1; UIDS[id] = 1;
} }
} }
@ -228,101 +190,70 @@ BEGIN {
function unescape(input, preserve_newlines) function unescape(input, preserve_newlines)
{ {
ret = gensub("\\\\,", ",", "g", ret = input
gensub("\\\\;", ";", "g", input)) gsub(/\\,/, ",", ret)
gsub(/\\;/, ";", ret)
if (preserve_newlines) if (preserve_newlines)
ret = gensub("\\\\n", "\n", "g", ret) gsub(/\\n/, "\n", ret)
else else
ret = gensub("\\\\n", " ", "g", ret) gsub(/\\n/, " ", ret)
return ret return ret
} }
function datetimestring(input, tz) function datetimestring(input, tzInput)
{ {
spec = match(input, "([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2}).*[\r]*", a); timestr = input
year = a[1] pos = index(timestr, "T")
month = a[2] if (pos < 0) {
day = a[3] return timestr
hour = a[4]
min = a[5]
sec = a[6]
stamp = mktime(year" "month" "day" "hour" "min" "sec);
if (input ~ /[0-9]{8}T[0-9]{6}Z/ ) {
tz = "UTC"
} }
return strftime("%Y-%m-%d %a %H:%M", stamp)" "tz; date = substr(timestr, 1, pos)
} time = substr(timestr, pos+1, length(timestr))
year = substr(date, 1, 4)
month = substr(date, 5, 2)
day = substr(date, 7, 2)
function datestring(input) hour = substr(time, 1, 2)
{ min = substr(time, 3, 2)
spec = gensub("([0-9]{4})([0-9]{2})([0-9]{2}).*[\r]*", "\\1 \\2 \\3 00 00 00", "g", input); sec = substr(time, 5, 2)
stamp = mktime(spec); return sprintf("%4d/%02d/%02d %02d:%02d:%02d %s", year, month, day, hour, min, sec, tzInput)
return strftime("%Y-%m-%d %a", stamp);
} }
function add_attendee(attendee) function add_attendee(attendee)
{ {
CN = find_full_name(attendee) CN = find_full_name(attendee)
if (CN != "")
people_attending[CN] = 1;
if (CN != "") { if (CN != "") {
match(attendee, /PARTSTAT=([^;]+)/, m) idx = idx + 1
{ people_attending[idx] = CN;
people_partstat[CN] = m[1] people_partstat[idx] = get_value(attendee, "PARTSTAT=[^;:]+", "=")
} people_rsvp[idx] = get_value(attendee, "RSVP=[^;:]+", "=")
match(attendee, /RSVP=([^;]+)/, m)
{
people_rsvp[CN] = m[1]
}
} }
} }
function extract_email(line)
{
email = ""
match(line,/:[ ]*(mailto|MAILTO):([^;]+)/, m)
{
email = m[2]
}
return email
}
function extract_name(line)
{
name = ""
match(line,/CN="?([^;:"]+)/, m)
{
name = m[1]
}
return name
}
function find_full_name(line) function find_full_name(line)
{ {
name = extract_name(line) name = get_value(line, "CN=[^;:]+", "=")
email = extract_email(line) email = get_value(line, "(mailto|MAILTO):[^;]+", ":")
if (name == "") { if (name == "") {
if (email == "") { return sprintf("<%s>", email)
return "" } else {
} else { return sprintf("%s <%s>", name, email)
return name }
}
function get_value(line, regexp, sep) {
value = ""
match(line, regexp)
{
z = split(substr(line,RSTART,RLENGTH),data,sep)
if (z > 1) {
value = data[2]
} }
} }
return value
if (email == "")
return name
if (email == name)
return "<"email">"
return name" <"email">"
} }