cleaned awk scripts, str escape bugfix, proper use of local variables

This commit is contained in:
Ämin Baumeler 2025-06-16 11:04:32 +02:00
parent ee02a7647b
commit 83beaa3ad5
12 changed files with 401 additions and 262 deletions

View File

@ -1,79 +1,102 @@
## src/awk/approx.awk
## Generate single-line approximate information for every iCalendar argument.
##
## @assign collection_labels: See configuration of the current program.
# Functions
# parse
# Time-zone aware parsing of the date/date-time entry at the current record.
#
# @local variables: dt
# @return: date or date-time string that can be used in date (1)
function parse( dt) { function parse( dt) {
# Get timezone information # Get timezone information
dt = "";
for (i=2; i<NF-1; i+=2) { for (i=2; i<NF-1; i+=2) {
if ($i == "TZID") { if ($i == "TZID") {
dt = "TZ=\"" $(i+1) "\" "; dt = "TZ=\"" $(i+1) "\" "
break; break
} }
} }
# Get date/datetime # Get date/date-time
return length($NF) == 8 ? return length($NF) == 8 ?
dt $NF : dt $NF :
dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF); dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF)
} }
function parse_duration( dt, dta, i, n, a, seps) { # parse_duration
n = split($NF, a, /[PTWHMSD]/, seps); # Map iCalendar duration specification into the format to be used in date (1).
delete dta; #
# @local variables: dt, dta, i, n, a, seps
# @input duration: iCalendar duration string
# @return: relative-date/date-time specification to be used in date (1)
function parse_duration(duration, dt, dta, i, n, a, seps) {
n = split(duration, a, /[PTWHMSD]/, seps)
for (i=2; i<=n; i++) { for (i=2; i<=n; i++) {
if(seps[i] == "W") dta["weeks"] = a[i]; if(seps[i] == "W") dta["weeks"] = a[i]
if(seps[i] == "H") dta["hours"] = a[i]; if(seps[i] == "H") dta["hours"] = a[i]
if(seps[i] == "M") dta["minutes"] = a[i]; if(seps[i] == "M") dta["minutes"] = a[i]
if(seps[i] == "S") dta["seconds"] = a[i]; if(seps[i] == "S") dta["seconds"] = a[i]
if(seps[i] == "D") dta["days"] = a[i]; if(seps[i] == "D") dta["days"] = a[i]
} }
dt = a[1] ? a[1] : "+"; dt = a[1] ? a[1] : "+"
for (i in dta) { for (i in dta)
dt = dt " " dta[i] " " i; dt = dt " " dta[i] " " i
} return dt
return dt;
} }
# fn
# Get relative file path.
#
# @local variables: n, a
# @input path: Path to file
# @return: File path of depth 1
function fn(path, n, a) { function fn(path, n, a) {
n = split(path, a, "/"); n = split(path, a, "/")
return a[n-1] "/" a[n]; return a[n-1] "/" a[n]
} }
# title
# Generate title string that will be displayed to user. Here, the start date
# gets a monthly resolution.
#
# @input start: Parsed content of DTSTART field
# @input summary: Content of SUMMARY field
# @return: colorized single-line title string
function title(start, summary) { function title(start, summary) {
summary = substr(summary, index(summary, ":") + 1); summary = substr(summary, index(summary, ":") + 1)
#gsub("\\\\n", "\n", summary); # one-liner gsub("\\\\n", " ", summary)
#gsub("\\\\N", "\n", summary); # one-liner gsub("\\\\N", " ", summary)
gsub("\\\\n", " ", summary); gsub("\\\\,", ",", summary)
gsub("\\\\N", " ", summary); gsub("\\\\;", ";", summary)
gsub("\\\\,", ",", summary); gsub("\\\\\\\\", "\\", summary)
gsub("\\\\;", ";", summary); gsub("\\|", ":", summary) # we use "|" as delimiter
gsub("\\\\\\\\", "\\", summary); depth = split(FILENAME, path, "/")
gsub("\\|", ":", summary); # we use "|" as delimiter collection = depth > 1 ? path[depth-1] : ""
depth = split(FILENAME, path, "/"); collection = collection in collection2label ? collection2label[collection] : collection
collection = depth > 1 ? path[depth-1] : "";
collection = collection in collection2label ? collection2label[collection] : collection;
return FAINT "~ " collection " " gensub(/^[^0-9]*([0-9]{4})([0-9]{2}).*$/, "\\1-\\2", "1", start) " " summary OFF return FAINT "~ " collection " " gensub(/^[^0-9]*([0-9]{4})([0-9]{2}).*$/, "\\1-\\2", "1", start) " " summary OFF
} }
# AWK program
BEGIN { BEGIN {
FS="[:;=]"; FS="[:;=]"
OFS="|" OFS="|"
split(collection_labels, mapping, ";"); split(collection_labels, mapping, ";")
for (map in mapping) for (map in mapping)
{ {
split(mapping[map], m, "="); split(mapping[map], m, "=")
collection2label[m[1]] = m[2]; collection2label[m[1]] = m[2]
} }
# Colors # Colors
GREEN = "\033[1;32m"; FAINT = "\033[2m"
RED = "\033[1;31m"; OFF = "\033[m"
WHITE = "\033[1;97m";
CYAN = "\033[1;36m";
FAINT = "\033[2m";
OFF = "\033[m";
} }
BEGINFILE { inside = 0; rs = 0; dur = 0; summary = ""; start = "ERROR"; end = "ERROR" } BEGINFILE { inside = 0; rs = 0; dur = 0; summary = ""; start = "ERROR"; end = "ERROR" }
/^END:VEVENT/ { print "~", start, dur ? start " " end : end, title(start, summary), fn(FILENAME, n, a); nextfile } /^END:VEVENT/ { print "~", start, dur ? start " " end : end, title(start, summary), fn(FILENAME); nextfile }
/^DTSTART/ && inside { start = parse( dt) } /^DTSTART/ && inside { start = parse() }
/^DTEND/ && inside { end = parse( dt) } /^DTEND/ && inside { end = parse() }
/^DURATION/ && inside { end = parse_duration( dt, dta, i, n, a, seps); dur = 1 } /^DURATION/ && inside { end = parse_duration($NF); dur = 1 }
/^[^ ]/ && rs { rs = 0 } /^[^ ]/ && rs { rs = 0 }
/^ / && rs { summary = summary substr($0, 2); } /^ / && rs { summary = summary substr($0, 2) }
/^SUMMARY/ && inside { rs = 1; summary = $0; } /^SUMMARY/ && inside { rs = 1; summary = $0 }
/^BEGIN:VEVENT/ { inside = 1 } /^BEGIN:VEVENT/ { inside = 1 }

View File

@ -1,3 +1,9 @@
## src/awk/calannot.awk
## Annotate monthly calendar
##
## @assign cur: Day-of-month to mark as `today`
## @assign day: Day-of-month to highlight
BEGIN { BEGIN {
BLACK = "\033[1;30m" BLACK = "\033[1;30m"
GREEN = "\033[1;32m" GREEN = "\033[1;32m"

View File

@ -1,3 +1,7 @@
## src/awk/calshift.awk
## Rearrange days of a monthly output from cal (1), such that Monday is the
## first day of the week.
BEGIN { BEGIN {
ORS = "" ORS = ""
W3 = " " W3 = " "

View File

@ -1,20 +1,69 @@
# $s|$e|$starttime|$endtime|$fpath|$collection|$description ## src/awk/dayview.awk
## Generate the view of a day from lines of the form
## ```
## <start_date>|<end_date>|<start_time>|<end_time>|<file_path>|<collection>|<description>
## ```.
##
## @assign today: Date of `today` in the format %D (%m/%d/%y)
## @assign daystart: Hour of start of the day
## @assign dayend: Hour of end of the day
# Functions
# allday
# Return line for all-day event.
#
# @input collection: Collection symbol
# @input desc: Event description
# @return: Single-line string
function allday(collection, desc) { function allday(collection, desc) {
return collection " " ITALIC FAINT " (allday) " OFF desc return collection " " ITALIC FAINT " (allday) " OFF desc
} }
# endstoday
# Return line for multi-day event, or event that starts at midnight, which ends today.
#
# @input stop: Time at which the event ends
# @input collection: Collection symbol
# @input desc: Event description
# @return: Single-line string
function endstoday(stop, collection, desc) { function endstoday(stop, collection, desc) {
return collection " " CYAN " -- " stop OFF ": " desc return collection " " CYAN " -- " stop OFF ": " desc
} }
# slice
# Return line for event that starts sometime today.
#
# @input start: Time at which the event starts
# @input stop: Time at which the event ends
# @input collection: Collection symbol
# @input desc: Event description
# @return: Single-line string
function slice(start, stop, collection, desc) { function slice(start, stop, collection, desc) {
if (stop == "00:00") if (stop == "00:00")
return collection " " CYAN start " -- " OFF ": " desc return collection " " CYAN start " -- " OFF ": " desc
else else
return collection " " CYAN start OFF " -- " CYAN stop OFF ": " desc return collection " " CYAN start OFF " -- " CYAN stop OFF ": " desc
} }
# hrline
# Print line for a single hour entry.
#
# @input hour: Hour of the entry
function hrline(hour) { function hrline(hour) {
hour = hour < 10 ? "0"hour : hour hour = hour < 10 ? "0"hour : hour
print today, hour, "", "", "", " " FAINT hour ":00 ----------------------" OFF print today, hour, "", "", "", " " FAINT hour ":00 ----------------------" OFF
} }
# hrlines
# Print lines for hour entries before an event that starts at `start` and stops
# at `stop`.
#
# @local variables: starth, stoph, tmp, i
# @input start: Time at which the event starts
# @input stop: Time at which the event ends
# @input h: Last event-free hour
# @return: Hour of now last event-free hour
function hrlines(start, stop, h, starth, stoph, tmp, i) { function hrlines(start, stop, h, starth, stoph, tmp, i) {
starth = substr(start, 1, 2) starth = substr(start, 1, 2)
stoph = substr(stop, 1, 2) stoph = substr(stop, 1, 2)
@ -27,11 +76,10 @@ function hrlines(start, stop, h, starth, stoph, tmp, i) {
else else
return stoph + tmp return stoph + tmp
} }
# AWK program
BEGIN { BEGIN {
FS = "|" FS = "|"
GREEN = "\033[1;32m"
RED = "\033[1;31m"
WHITE = "\033[1;97m"
CYAN = "\033[1;36m" CYAN = "\033[1;36m"
ITALIC = "\033[3m" ITALIC = "\033[3m"
FAINT = "\033[2m" FAINT = "\033[2m"
@ -41,9 +89,7 @@ BEGIN {
$1 == "00:00" && $2 == "00:00" { print today, $1, $3, $4, $5, allday($6, $7); next } $1 == "00:00" && $2 == "00:00" { print today, $1, $3, $4, $5, allday($6, $7); next }
$1 == "00:00" { print today, $1, $3, $4, $5, endstoday($2, $6, $7); next } $1 == "00:00" { print today, $1, $3, $4, $5, endstoday($2, $6, $7); next }
$1 ~ /^[0-9]{2}:[0-9]{2}$/ { $1 ~ /^[0-9]{2}:[0-9]{2}$/ {
daystart = hrlines($1, $2, daystart, starth, stoph, tmp, i) daystart = hrlines($1, $2, daystart)
print today, $1, $3, $4, $5, slice($1, $2, $6, $7) print today, $1, $3, $4, $5, slice($1, $2, $6, $7)
} }
END { END { hrlines(dayend":00", 0, daystart) }
hrlines(dayend":00", 0, daystart, starth, stoph, tmp, i)
}

View File

@ -1,18 +1,23 @@
# print content of field `field` ## src/awk/get.awk
BEGIN { FS = ":"; regex = "^" field; } ## Print content of a field of an iCalendar file.
##
## @assign field: Field name
# AWK program
BEGIN { FS = ":"; regex = "^" field }
/^BEGIN:VEVENT$/ { inside = 1 } /^BEGIN:VEVENT$/ { inside = 1 }
/^END:VEVENT$/ { exit } /^END:VEVENT$/ { exit }
$0 ~ regex { content = $0; next; } $0 ~ regex { content = $0; next }
/^ / && content { content = content substr($0, 2); next; } /^ / && content { content = content substr($0, 2); next }
/^[^ ]/ && content { exit } /^[^ ]/ && content { exit }
END { END {
if (!inside) { exit } if (!inside) { exit }
# Process content line # Process content line
content = substr(content, index(content, ":") + 1); content = substr(content, index(content, ":") + 1)
gsub("\\\\n", "\n", content); gsub("\\\\n", "\n", content)
gsub("\\\\N", "\n", content); gsub("\\\\N", "\n", content)
gsub("\\\\,", ",", content); gsub("\\\\,", ",", content)
gsub("\\\\;", ";", content); gsub("\\\\;", ";", content)
gsub("\\\\\\\\", "\\", content); gsub("\\\\\\\\", "\\", content)
print content; print content
} }

View File

@ -1,9 +1,15 @@
## src/awk/merge.awk
## Merge a file that contains pairs of lines for start and end dates of events
## with the approximate data file, and group the iCalendar file paths according
## to the weeks at which the events take place.
# AWK program
BEGIN { FS="|" } BEGIN { FS="|" }
NR == FNR { NR == FNR {
i = i + 1; i = i + 1
from_year[i] = $1 from_year[i] = $1
from_week[i] = $2 from_week[i] = $2
getline; getline
to_year[i] = $1 to_year[i] = $1
to_week[i] = $2 to_week[i] = $2
next next
@ -19,9 +25,9 @@ NR == FNR {
week[label] = week[label] " " $5 week[label] = week[label] " " $5
week_i++ week_i++
if (week_i > 53) { if (week_i > 53) {
week_ = 1 week_i = 1
year_i++ year_i++
} }
} }
} }
END { for (label in week) print label week[label]; } END { for (label in week) print label week[label] }

View File

@ -1,36 +1,57 @@
## src/awk/new.awk
## Generate iCalendar file from markdown description.
##
## @assign uid: UID to use
# Functions
# escape
# Escape string to be used as content in iCalendar files.
#
# @input str: String to escape
# @return: Escaped string
function escape(str) function escape(str)
{ {
gsub("\\\\", "\\\\", str); gsub("\\\\", "\\\\", str)
gsub(";", "\\\\;", str); gsub(";", "\\\\;", str)
gsub(",", "\\\\,", str); gsub(",", "\\\\,", str)
return str
} }
# print_fold
# Print property with its content and fold according to the iCalendar
# specification.
#
# @local variables: i, s
# @input nameparam: Property name with optional parameters
# @input content: Escaped content
function print_fold(nameparam, content, i, s) function print_fold(nameparam, content, i, s)
{ {
i = 74 - length(nameparam); i = 74 - length(nameparam)
s = substr(content, 1, i); s = substr(content, 1, i)
print nameparam s; print nameparam s
s = substr(content, i+1, 73); s = substr(content, i+1, 73)
i = i + 73; i = i + 73
while (s) while (s)
{ {
print " " s; print " " s
s = substr(content, i+1, 73); s = substr(content, i+1, 73)
i = i + 73; i = i + 73
} }
} }
# AWK program
BEGIN { BEGIN {
FS=":"; FS=":"
zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1); zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1)
} }
desc { desc = desc "\\n" $0; next; } desc { desc = desc "\\n" $0; next }
{ {
from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""; from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""
if (!from) if (!from)
exit 1 exit 1
getline getline
to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : ""; to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : ""
if (!to) if (!to)
exit 1 exit 1
getline getline
@ -41,28 +62,28 @@ desc { desc = desc "\\n" $0; next; }
exit 1 exit 1
getline # This line should be empty getline # This line should be empty
getline # First line of description getline # First line of description
desc = $0; desc = $0
next; next
} }
END { END {
# Sanitize input # Sanitize input
# If nanoseconds are not 0, then we assume user enterd "tomorrow" or # If nanoseconds are not 0, then we assume user entered "tomorrow" or
# something the like, and we make this a date entry, as opposed to a # something the like, and we make this a date entry, as opposed to a
# date-time entry. # date-time entry.
# Similalry, if the time is 00:00, we make this a date, as opposed to a # Similarly, if the time is 00:00, we make this a date, as opposed to a
# date-time entry. # date-time entry.
gsub("\"", "\\\"", from) gsub("\"", "\\\"", from)
cmd = "date -d \"" from "\" +\"%N\""; cmd = "date -d \"" from "\" +\"%N\""
cmd | getline n cmd | getline n
close(cmd) close(cmd)
n = n + 0 n = n + 0
cmd = "date -d \"" from "\" +\"%H%M\""; cmd = "date -d \"" from "\" +\"%H%M\""
cmd | getline t cmd | getline t
close(cmd) close(cmd)
t = t + 0 t = t + 0
if (n != 0 || t == 0) { if (n != 0 || t == 0) {
from_type = "DATE" from_type = "DATE"
cmd = "date -d \"" from "\" +\"%Y%m%d\""; cmd = "date -d \"" from "\" +\"%Y%m%d\""
} else { } else {
from_type = "DATE-TIME" from_type = "DATE-TIME"
cmd = "date -d \"" from "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d" cmd = "date -d \"" from "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
@ -74,17 +95,17 @@ END {
} }
# #
gsub("\"", "\\\"", to) gsub("\"", "\\\"", to)
cmd = "date -d \"" to "\" +\"%N\""; cmd = "date -d \"" to "\" +\"%N\""
cmd | getline n cmd | getline n
close(cmd) close(cmd)
n = n + 0 n = n + 0
cmd = "date -d \"" to "\" +\"%H%M\""; cmd = "date -d \"" to "\" +\"%H%M\""
cmd | getline t cmd | getline t
close(cmd) close(cmd)
t = t + 0 t = t + 0
if (n != 0 || t == 0) { if (n != 0 || t == 0) {
to_type = "DATE" to_type = "DATE"
cmd = "date -d \"" to "\" +\"%Y%m%d\""; cmd = "date -d \"" to "\" +\"%Y%m%d\""
} else { } else {
to_type = "DATE-TIME" to_type = "DATE-TIME"
cmd = "date -d \"" to "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d" cmd = "date -d \"" to "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
@ -94,28 +115,28 @@ END {
if (suc != 1) { if (suc != 1) {
exit 1 exit 1
} }
escape(summary); summary = escape(summary)
escape(location); location = escape(location)
escape(desc); desc = escape(desc)
# print ical # print ical
print "BEGIN:VCALENDAR"; print "BEGIN:VCALENDAR"
print "VERSION:2.0"; print "VERSION:2.0"
print "CALSCALE:GREGORIAN"; print "CALSCALE:GREGORIAN"
print "PRODID:-//fab//awk//EN"; print "PRODID:-//fab//awk//EN"
print "BEGIN:VEVENT" print "BEGIN:VEVENT"
print "DTSTAMP:" zulu; print "DTSTAMP:" zulu
print "UID:" uid; print "UID:" uid
print "CLASS:PRIVATE"; print "CLASS:PRIVATE"
print "CREATED:" zulu; print "CREATED:" zulu
print "SEQUENCE:1"; print "SEQUENCE:1"
print "LAST-MODIFIED:" zulu; print "LAST-MODIFIED:" zulu
print "STATUS:VEVENT"; print "STATUS:VEVENT"
print "DTSTART;VALUE=" from_type ":" from print "DTSTART;VALUE=" from_type ":" from
print "DTEND;VALUE=" to_type ":" to print "DTEND;VALUE=" to_type ":" to
if (summary) print_fold("SUMMARY:", summary, i, s); if (summary) print_fold("SUMMARY:", summary)
if (desc) print_fold("DESCRIPTION:", desc, i, s); if (desc) print_fold("DESCRIPTION:", desc)
if (location) print_fold("LOCATION:", location, i, s); if (location) print_fold("LOCATION:", location)
print "END:VEVENT" print "END:VEVENT"
print "END:VCALENDAR" print "END:VCALENDAR"
} }

View File

@ -1,46 +1,72 @@
## src/awk/parse.awk
## Parse iCalendar file and print its key aspects:
## ```
## <start> <end> <fpath> <collection> <summary>
## ```.
##
## @assign collection_labels: See configuration of the current program.
# Functions
# parse
# Time-zone aware parsing of the date/date-time entry at the current record.
#
# @local variables: dt
# @return: date or date-time string that can be used in date (1)
function parse( dt) { function parse( dt) {
# Get timezone information # Get timezone information
dt = "";
for (i=2; i<NF-1; i+=2) { for (i=2; i<NF-1; i+=2) {
if ($i == "TZID") { if ($i == "TZID") {
dt = "TZ=\"" $(i+1) "\" "; dt = "TZ=\"" $(i+1) "\" "
break; break
} }
} }
# Get date/datetime # Get date/date-time
return length($NF) == 8 ? return length($NF) == 8 ?
dt $NF : dt $NF :
dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF); dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF)
} }
function parse_duration( dt, dta, i, n, a, seps) { # parse_duration
n = split($NF, a, /[PTWHMSD]/, seps); # Map iCalendar duration specification into the format to be used in date (1).
delete dta; #
# @local variables: dt, dta, i, n, a, seps
# @input duration: iCalendar duration string
# @return: relative-date/date-time specification to be used in date (1)
function parse_duration(duration, dt, dta, i, n, a, seps) {
n = split(duration, a, /[PTWHMSD]/, seps)
for (i=2; i<=n; i++) { for (i=2; i<=n; i++) {
if(seps[i] == "W") dta["weeks"] = a[i]; if(seps[i] == "W") dta["weeks"] = a[i]
if(seps[i] == "H") dta["hours"] = a[i]; if(seps[i] == "H") dta["hours"] = a[i]
if(seps[i] == "M") dta["minutes"] = a[i]; if(seps[i] == "M") dta["minutes"] = a[i]
if(seps[i] == "S") dta["seconds"] = a[i]; if(seps[i] == "S") dta["seconds"] = a[i]
if(seps[i] == "D") dta["days"] = a[i]; if(seps[i] == "D") dta["days"] = a[i]
} }
dt = a[1] ? a[1] : "+"; dt = a[1] ? a[1] : "+"
for (i in dta) { for (i in dta)
dt = dt " " dta[i] " " i; dt = dt " " dta[i] " " i
} return dt
return dt;
} }
function print_data(start, dur, end, summary, cmd, collection) { # print_data
summary = substr(summary, index(summary, ":") + 1); # Print string of parsed data.
gsub("\\\\n", " ", summary); # one-liner #
gsub("\\\\N", " ", summary); # one-liner # @local variables: cmd, collection, depth, path
gsub("\\\\,", ",", summary); # @input start: Start time of event
gsub("\\\\;", ";", summary); # @input dur: Boolean that indicates that `end` specifies a duration
gsub("\\\\\\\\", "\\", summary); # @input end: End time of event, or event duration (see `dur`)
depth = split(FILENAME, path, "/"); # @input summary: Content of SUMMARY field of the event
function print_data(start, dur, end, summary, cmd, collection, depth, path) {
summary = substr(summary, index(summary, ":") + 1)
gsub("\\\\n", " ", summary) # one-liner
gsub("\\\\N", " ", summary) # one-liner
gsub("\\\\,", ",", summary)
gsub("\\\\;", ";", summary)
gsub("\\\\\\\\", "\\", summary)
depth = split(FILENAME, path, "/")
fpath = path[depth-1] "/" path[depth] fpath = path[depth-1] "/" path[depth]
collection = depth > 1 ? path[depth-1] : ""; collection = depth > 1 ? path[depth-1] : ""
collection = collection in collection2label ? collection2label[collection] : collection; collection = collection in collection2label ? collection2label[collection] : collection
collection = collection2label[path[depth-1]] collection = collection2label[path[depth-1]]
end = dur ? start " " end : end end = dur ? start " " end : end
cmd = "date -d '" start "' +\"%s\"" cmd = "date -d '" start "' +\"%s\""
@ -52,20 +78,21 @@ function print_data(start, dur, end, summary, cmd, collection) {
print start, end, fpath, collection, summary print start, end, fpath, collection, summary
} }
# AWK program
BEGIN { BEGIN {
FS="[:;=]"; FS="[:;=]"
split(collection_labels, mapping, ";"); split(collection_labels, mapping, ";")
for (map in mapping) for (map in mapping)
{ {
split(mapping[map], m, "="); split(mapping[map], m, "=")
collection2label[m[1]] = m[2]; collection2label[m[1]] = m[2]
} }
} }
/^END:VEVENT/ && inside { print_data(start, dur, end, summary, cmd, collection); exit } /^END:VEVENT/ && inside { print_data(start, dur, end, summary); exit }
/^DTSTART/ && inside { start = parse( dt) } /^DTSTART/ && inside { start = parse() }
/^DTEND/ && inside { end = parse( dt) } /^DTEND/ && inside { end = parse() }
/^DURATION/ && inside { end = parse_duration( dt, dta, i, n, a, seps); dur = 1 } /^DURATION/ && inside { end = parse_duration($NF); dur = 1 }
/^[^ ]/ && rs { rs = 0 } /^[^ ]/ && rs { rs = 0 }
/^ / && rs { summary = summary substr($0, 2); } /^ / && rs { summary = summary substr($0, 2) }
/^SUMMARY/ && inside { rs = 1; summary = $0; } /^SUMMARY/ && inside { rs = 1; summary = $0 }
/^BEGIN:VEVENT/ { inside = 1 } /^BEGIN:VEVENT/ { inside = 1 }

View File

@ -1,3 +1,18 @@
## src/awk/set.awk
## Set or replace the content of a specified field in the iCalendar file.
##
## @assign field: iCalendar field
## @assign value: Content to set it to
##
## LIMITATION: This program does not fold long content lines.
# Functions
# escape
# Escape string to be used as content.
#
# @input str: Content string
# @return: Escaped string
function escape(str) function escape(str)
{ {
gsub("\\\\", "\\\\", str) gsub("\\\\", "\\\\", str)
@ -5,9 +20,12 @@ function escape(str)
gsub(",", "\\\\,", str) gsub(",", "\\\\,", str)
return str return str
} }
BEGIN { FS = "[:;]"; }
# AWK program
BEGIN { FS = "[:;]" }
/^BEGIN:VEVENT$/ { inside = 1 } /^BEGIN:VEVENT$/ { inside = 1 }
/^END:VEVENT$/ { inside = 0 } /^END:VEVENT$/ { inside = 0; if (!duplic) print field ":" escape(value) }
$1 == field && inside { con = 1; duplic = 1; print field ":" escape(value); next } $1 == field && inside { con = 1; duplic = 1; print field ":" escape(value); next }
$1 == field && duplic { con = 1; next } $1 == field && duplic { con = 1; next }
/^ / && con { next } /^ / && con { next }

View File

@ -1,53 +1,68 @@
function getcontent(content_line, prop) ## src/awk/update.awk
{ ## Update iCalendar file from markdown file.
return substr(content_line[prop], index(content_line[prop], ":") + 1);
}
# Functions
# escape
# Escape string to be used as content in iCalendar files.
#
# @input str: String to escape
# @return: Escaped string
function escape(str) function escape(str)
{ {
gsub("\\\\", "\\\\", str); gsub("\\\\", "\\\\", str)
gsub(";", "\\\\;", str); gsub(";", "\\\\;", str)
gsub(",", "\\\\,", str); gsub(",", "\\\\,", str)
return str
} }
# print_fold
# Print property with its content and fold according to the iCalendar
# specification.
#
# @local variables: i, s
# @input nameparam: Property name with optional parameters
# @input content: Escaped content
function print_fold(nameparam, content, i, s) function print_fold(nameparam, content, i, s)
{ {
i = 74 - length(nameparam); i = 74 - length(nameparam)
s = substr(content, 1, i); s = substr(content, 1, i)
print nameparam s; print nameparam s
s = substr(content, i+1, 73); s = substr(content, i+1, 73)
i = i + 73; i = i + 73
while (s) while (s)
{ {
print " " s; print " " s
s = substr(content, i+1, 73); s = substr(content, i+1, 73)
i = i + 73; i = i + 73
} }
} }
# AWK program
BEGIN { BEGIN {
FS=":"; FS=":"
zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1); zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1)
} }
ENDFILE { ENDFILE {
if (NR == FNR) if (NR == FNR)
{ {
# If nanoseconds are not 0, then we assume user enterd "tomorrow" or # If nanoseconds are not 0, then we assume user entered "tomorrow" or
# something the like, and we make this a date entry, as opposed to a # something the like, and we make this a date entry, as opposed to a
# date-time entry. # date-time entry.
gsub("\"", "\\\"", from) gsub("\"", "\\\"", from)
cmd = "date -d \"" from "\" +\"%N\""; cmd = "date -d \"" from "\" +\"%N\""
cmd | getline n cmd | getline n
close(cmd) close(cmd)
n = n + 0 n = n + 0
cmd = "date -d \"" from "\" +\"%H%M\""; cmd = "date -d \"" from "\" +\"%H%M\""
cmd | getline t cmd | getline t
close(cmd) close(cmd)
t = t + 0 t = t + 0
if (n != 0 || t == 0) { if (n != 0 || t == 0) {
from_type = "DATE" from_type = "DATE"
cmd = "date -d \"" from "\" +\"%Y%m%d\""; cmd = "date -d \"" from "\" +\"%Y%m%d\""
} else { } else {
from_type = "DATE-TIME" from_type = "DATE-TIME"
cmd = "date -d \"" from "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d" cmd = "date -d \"" from "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
@ -59,17 +74,17 @@ ENDFILE {
} }
# #
gsub("\"", "\\\"", to) gsub("\"", "\\\"", to)
cmd = "date -d \"" to "\" +\"%N\""; cmd = "date -d \"" to "\" +\"%N\""
cmd | getline n cmd | getline n
close(cmd) close(cmd)
n = n + 0 n = n + 0
cmd = "date -d \"" to "\" +\"%H%M\""; cmd = "date -d \"" to "\" +\"%H%M\""
cmd | getline t cmd | getline t
close(cmd) close(cmd)
t = t + 0 t = t + 0
if (n != 0 || t == 0) { if (n != 0 || t == 0) {
to_type = "DATE" to_type = "DATE"
cmd = "date -d \"" to "\" +\"%Y%m%d\""; cmd = "date -d \"" to "\" +\"%Y%m%d\""
} else { } else {
to_type = "DATE-TIME" to_type = "DATE-TIME"
cmd = "date -d \"" to "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d" cmd = "date -d \"" to "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
@ -79,19 +94,19 @@ ENDFILE {
if (suc != 1) { if (suc != 1) {
exit 1 exit 1
} }
escape(summary); summary = escape(summary)
escape(location); location = escape(location)
escape(desc); desc = escape(desc)
} }
} }
NR == FNR && desc { desc = desc "\\n" $0; next; } NR == FNR && desc { desc = desc "\\n" $0; next }
NR == FNR { NR == FNR {
from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""; from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""
if (!from) if (!from)
exit 1 exit 1
getline getline
to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : ""; to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : ""
if (!to) if (!to)
exit 1 exit 1
getline getline
@ -102,8 +117,8 @@ NR == FNR {
exit 1 exit 1
getline # This line should be empty getline # This line should be empty
getline # First line of description getline # First line of description
desc = $0; desc = $0
next; next
} }
/^BEGIN:VEVENT$/ { inside = 1; print; next } /^BEGIN:VEVENT$/ { inside = 1; print; next }
@ -117,9 +132,9 @@ NR == FNR {
print "LAST-MODIFIED:" zulu print "LAST-MODIFIED:" zulu
print "DTSTART;VALUE=" from_type ":" from print "DTSTART;VALUE=" from_type ":" from
print "DTEND;VALUE=" to_type ":" to print "DTEND;VALUE=" to_type ":" to
print_fold("SUMMARY:", summary, i, s) print_fold("SUMMARY:", summary)
print_fold("DESCRIPTION:", desc, i, s) print_fold("DESCRIPTION:", desc)
print_fold("LOCATION:", location, i, s) print_fold("LOCATION:", location)
inside = "" inside = ""
} }
{ print } { print }

View File

@ -1,44 +0,0 @@
function parse( dt) {
# Get timezone information
dt = "";
for (i=2; i<NF-1; i+=2) {
if ($i == "TZID") {
dt = "TZ=\"" $(i+1) "\" ";
break;
}
}
# Get date/datetime
return length($NF) == 8 ?
dt $NF :
dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF);
}
function parse_duration( dt, dta, i, n, a, seps) {
n = split($NF, a, /[PTWHMSD]/, seps);
delete dta;
for (i=2; i<=n; i++) {
if(seps[i] == "W") dta["weeks"] = a[i];
if(seps[i] == "H") dta["hours"] = a[i];
if(seps[i] == "M") dta["minutes"] = a[i];
if(seps[i] == "S") dta["seconds"] = a[i];
if(seps[i] == "D") dta["days"] = a[i];
}
dt = a[1] ? a[1] : "+";
for (i in dta) {
dt = dt " " dta[i] " " i;
}
return dt;
}
function fn(path, n, a) {
n = split(path, a, "/");
return a[n-1] "/" a[n];
}
BEGIN { FS="[:;=]"; OFS="|" }
BEGINFILE { inside = 0; dur = 0; start = "ERROR"; end = "ERROR" }
/^END:VEVENT/ { print start, dur ? start " " end : end, fn(FILENAME, n, a); nextfile }
/^DTSTART/ && inside { start = parse( dt) }
/^DTEND/ && inside { end = parse( dt) }
/^DURATION/ && inside { end = parse_duration( dt, dta, i, n, a, seps); dur = 1 }
/^BEGIN:VEVENT/ { inside = 1 }

View File

@ -1,23 +1,35 @@
## src/awk/weekview.awk
## Print view of all appointments of the current week.
##
## @assign startofweek: Date of first day in the week
# Functions
# c
# Compose line that will display a day in the week.
#
# @return: Single-line string
function c() { function c() {
return CYAN substr($0, index($0, ">") + 1) OFF " " RED "/" OFF return CYAN substr($0, index($0, ">") + 1) OFF " " RED "/" OFF
} }
# AWK program
BEGIN { BEGIN {
GREEN = "\033[1;32m"; GREEN = "\033[1;32m"
RED = "\033[1;31m"; RED = "\033[1;31m"
WHITE = "\033[1;97m"; CYAN = "\033[1;36m"
CYAN = "\033[1;36m"; OFF = "\033[m"
FAINT = "\033[2m";
OFF = "\033[m";
OFS = "|" OFS = "|"
} }
/^[0-7] 00:00 -- 00:00/ { dayline = dayline " " c(); next } /^[0-7] 00:00 -- 00:00/ { dayline = dayline " " c(); next }
/^[0-7] 00:00 -- / { dayline = dayline " <--" $4 " " c(); next } /^[0-7] 00:00 -- / { dayline = dayline " <--" $4 " " c(); next }
/^[0-7] [0-9]{2}:[0-9]{2} -- 00:00/ { dayline = dayline " " $2 "→" c(); next } /^[0-7] [0-9]{2}:[0-9]{2} -- 00:00/ { dayline = dayline " " $2 "→" c(); next }
/^[0-7] [0-9]{2}:[0-9]{2} -- [0-9]{2}:[0-9]{2}/ { dayline = dayline " " $2 " - " $4 " " c(); next } /^[0-7] [0-9]{2}:[0-9]{2} -- [0-9]{2}:[0-9]{2}/ { dayline = dayline " " $2 " - " $4 " " c(); next }
/^[0-7]$/ && dayline { print "+", startofweek " +" $0-1 " days", "", dayline; } /^[0-7]$/ && dayline { print "+", startofweek " +" $0-1 " days", "", dayline }
/^[0-7]$/ { /^[0-7]$/ {
cmd = "date -d '" startofweek " +" $0 " days' +\"%a %e %b %Y\""; cmd = "date -d '" startofweek " +" $0 " days' +\"%a %e %b %Y\""
cmd | getline dayline; cmd | getline dayline
close(cmd); close(cmd)
dayline = GREEN dayline ": " OFF dayline = GREEN dayline ": " OFF
} }