Compare commits
	
		
			1 Commits
		
	
	
		
			main
			...
			2cd4b02248
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2cd4b02248 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1 @@
 | 
				
			|||||||
fzf-vcal
 | 
					fzf-vcal
 | 
				
			||||||
fzf-vcal.debug
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										96
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								README.md
									
									
									
									
									
								
							@@ -63,81 +63,37 @@ item_types = ["VEVENT"]
 | 
				
			|||||||
...
 | 
					...
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Here is the complete list of configuration options:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
###   ROOT:                 Directory containing the collections
 | 
					 | 
				
			||||||
###   COLLECTION_LABELS:    Mappings between collections and labels
 | 
					 | 
				
			||||||
###   SYNC_CMD (optional):  Synchronization command
 | 
					 | 
				
			||||||
###   DAY_START (optional): Hour of start of the day (defaults to 8)
 | 
					 | 
				
			||||||
###   DAY_END (optional):   Hour of end of the day (defaults to 18)
 | 
					 | 
				
			||||||
###   EDITOR (optional):    Your favorite editor, is usually already exported
 | 
					 | 
				
			||||||
###   TZ (optional):        Your favorite timezone, usually system's choice
 | 
					 | 
				
			||||||
###   LC_TIME (optional):   Your favorite locale for date and time
 | 
					 | 
				
			||||||
###   ZI_DIR (optional):    Location of tzdata, defaults to /usr/share/zoneinfo
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Usage
 | 
					Usage
 | 
				
			||||||
-----
 | 
					-----
 | 
				
			||||||
 | 
					Use the default `fzf` keys to navigate your calendar entries, e.g., `ctrl-j`
 | 
				
			||||||
 | 
					and `ctrl-k` for going down/up in the list.
 | 
				
			||||||
After starting `fzf-vcal`, you are presented with a view on the current week.
 | 
					After starting `fzf-vcal`, you are presented with a view on the current week.
 | 
				
			||||||
You can navigate that week using `j` and `h` for going down and up.
 | 
					 | 
				
			||||||
Hit `<enter>` on any day, and you will see all entries for that date, including
 | 
					Hit `<enter>` on any day, and you will see all entries for that date, including
 | 
				
			||||||
previews. In both, the week and day views, you can add entries by hitting
 | 
					previews. In both, the week and day views, you can add entries by hitting
 | 
				
			||||||
`ctrl-n`. 
 | 
					`ctrl-n`. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Here is the list of all available keybindings:
 | 
					Here is the list of available keybindings:
 | 
				
			||||||
 | 
					| Key | View | Action |
 | 
				
			||||||
### Week view
 | 
					| --- | ---- | ------ |
 | 
				
			||||||
 | 
					| `enter` | week view | Switch to day view |
 | 
				
			||||||
| Key | Action |
 | 
					| `ctrl-n` | week view | Make a new entry |
 | 
				
			||||||
| --- | ------ |
 | 
					| any letter | week view | Search in the list of all entries |
 | 
				
			||||||
| `q` | quit |
 | 
					| `backspace` on empty query | week view | Undo search |
 | 
				
			||||||
| `enter` | open day |
 | 
					| `ctrl-u` | week view | Go back one week |
 | 
				
			||||||
| `j` | down |
 | 
					| `ctrl-d` | week view | Go forth one week |
 | 
				
			||||||
| `k` | up |
 | 
					| `ctrl-alt-u` | week view | Go back one month |
 | 
				
			||||||
| `l` | go to next week |
 | 
					| `ctrl-alt-d` | week view | Go forth one month |
 | 
				
			||||||
| `h` | go to previous week |
 | 
					| `ctrl-s` | week view | Run the synchronization command |
 | 
				
			||||||
| `ctrl-l` | go to next month |
 | 
					| `ctrl-l` | week view | Go to current week |
 | 
				
			||||||
| `ctrl-h` | go to previous month |
 | 
					| `ctrl-g` | week view | Goto date |
 | 
				
			||||||
| `alt-l` | go to next year |
 | 
					| `enter` | day view | Open selected  calendar entry in your favorite `$EDITOR` |
 | 
				
			||||||
| `alt-h` | go to previous year |
 | 
					| `ctrl-n` | day view | Make a new entry |
 | 
				
			||||||
| `ctrl-r` | reload and go to week that contains `today` |
 | 
					| `esc`, `backspace` or `q` | day view | Go back to week view |
 | 
				
			||||||
| `ctrl-g` | interactively go to specified week |
 | 
					| `ctrl-s` | day view | Run the synchronization command |
 | 
				
			||||||
| `ctrl-t` | set timezon |
 | 
					| `ctrl-alt-d` | day view | Delete selected entry |
 | 
				
			||||||
| `ctrl-s` | synchronize |
 | 
					| `j` | day view | Scroll down in preview window |
 | 
				
			||||||
| `ctrl-n` | add new entry |
 | 
					| `k` | day view | Scroll up in preview window |
 | 
				
			||||||
| `\` | search all appointment s |
 | 
					| `w` | day view | Toggle line wrap in preview window ||
 | 
				
			||||||
| `x` | Cancel and confirm entry |
 | 
					 | 
				
			||||||
| `c` | Unconfirm and confirm entry |
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Day view
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| Key | Action |
 | 
					 | 
				
			||||||
| --- | ------ |
 | 
					 | 
				
			||||||
| `enter` | edit appointment |
 | 
					 | 
				
			||||||
| `a` | open attachment list of appointment |
 | 
					 | 
				
			||||||
| `j` | down |
 | 
					 | 
				
			||||||
| `k` | up |
 | 
					 | 
				
			||||||
| `l` | go to next day |
 | 
					 | 
				
			||||||
| `h` | go to previous day |
 | 
					 | 
				
			||||||
| `ctrl-l` | go to next week |
 | 
					 | 
				
			||||||
| `ctrl-h` | go to previous week |
 | 
					 | 
				
			||||||
| `alt-l` | go to next month |
 | 
					 | 
				
			||||||
| `alt-h` | go to previous month |
 | 
					 | 
				
			||||||
| `ctrl-r` | reload and go to `today` |
 | 
					 | 
				
			||||||
| `ctrl-g` | interactively go to specified day |
 | 
					 | 
				
			||||||
| `ctrl-t` | set timezon |
 | 
					 | 
				
			||||||
| `ctrl-s` | synchronize |
 | 
					 | 
				
			||||||
| `ctrl-n` | add new entry |
 | 
					 | 
				
			||||||
| `ctrl-alt-d` | delete entry |
 | 
					 | 
				
			||||||
| `w` | toggle line wrap in preview |
 | 
					 | 
				
			||||||
| `ctrl-d` | down in preview |
 | 
					 | 
				
			||||||
| `ctrl-u` | up in preview |
 | 
					 | 
				
			||||||
| `alt-v` | view raw iCalendar file |
 | 
					 | 
				
			||||||
| `esc` | return to week view, you can also do this with `q` or `backspace` |
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### There is more
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
You may also invoke the script with `--help` to see further command-line options. 
 | 
					You may also invoke the script with `--help` to see further command-line options. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -145,10 +101,6 @@ Also, you may set `LC_TIME` to your preferred language, and `TZ` to your
 | 
				
			|||||||
preferred timezone. The latter is in particular helpful if you want to take a
 | 
					preferred timezone. The latter is in particular helpful if you want to take a
 | 
				
			||||||
look at your calendar relative to being in another timezone.
 | 
					look at your calendar relative to being in another timezone.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Git support
 | 
					 | 
				
			||||||
-----------
 | 
					 | 
				
			||||||
You can track your events with `git` by simply running `fzf-vcal --git-init`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
License
 | 
					License
 | 
				
			||||||
-------
 | 
					-------
 | 
				
			||||||
This project is licensed under the [MIT License](./LICENSE).
 | 
					This project is licensed under the [MIT License](./LICENSE).
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,15 +5,7 @@ GREEN="\033[0;32m"
 | 
				
			|||||||
OFF="\033[m"
 | 
					OFF="\033[m"
 | 
				
			||||||
NAME="fzf-vcal"
 | 
					NAME="fzf-vcal"
 | 
				
			||||||
SRC="./src/main.sh"
 | 
					SRC="./src/main.sh"
 | 
				
			||||||
 | 
					echo "🐔 ${GREEN}Building${OFF} ${BOLD}$NAME${OFF}"
 | 
				
			||||||
tmpdir=$(mktemp -d)
 | 
					sed -E 's|@@include (.+)$|cat \1|e' "$SRC" >"$NAME"
 | 
				
			||||||
echo "🐔 ${GREEN}Internalize sourced files${OFF}"
 | 
					 | 
				
			||||||
sed -E 's|\. "([^$].+)"$|cat src/\1|e' "$SRC" >"$tmpdir/1.sh"
 | 
					 | 
				
			||||||
echo "🥚 ${GREEN}Internalize awk scripts${OFF}"
 | 
					 | 
				
			||||||
sed -E 's|@@include (.+)$|cat src/\1|e' "$tmpdir/1.sh" >"$tmpdir/2.sh"
 | 
					 | 
				
			||||||
echo "🐔 ${GREEN}Internalize awk libraries${OFF}"
 | 
					 | 
				
			||||||
sed -E 's|@include "(.+)"$|cat src/\1|e' "$tmpdir/2.sh" >"$NAME"
 | 
					 | 
				
			||||||
echo "🥚 ${GREEN}Make executable and cleanup${OFF}"
 | 
					 | 
				
			||||||
chmod +x "$NAME"
 | 
					chmod +x "$NAME"
 | 
				
			||||||
rm -rf "$tmpdir"
 | 
					echo "🥚 ${GREEN}Done${OFF}"
 | 
				
			||||||
echo "🍳 ${GREEN}Done:${OFF} Sucessfully built ${BOLD}${GREEN}$NAME${OFF}"
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,67 +0,0 @@
 | 
				
			|||||||
## src/awk/approx.awk
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## Generate single-line approximate information for every iCalendar argument.
 | 
					 | 
				
			||||||
## The fields in each line are separated by "\t"
 | 
					 | 
				
			||||||
## The fields are the following:
 | 
					 | 
				
			||||||
## 1. "~" (constant, indicating that the lines contains approximate information)
 | 
					 | 
				
			||||||
## 2. start (this can be used in date (1))
 | 
					 | 
				
			||||||
## 3. end (this can be used in date (1)
 | 
					 | 
				
			||||||
## 4. string to display
 | 
					 | 
				
			||||||
## 5. filename (collection/name)
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## @assign collection_labels: See configuration of the current program.
 | 
					 | 
				
			||||||
## @assign style_line: Style for each line
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@include "lib/awk/icalendar.awk"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Functions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Get relative file path.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @local variables: n, a
 | 
					 | 
				
			||||||
# @input path: Path to file
 | 
					 | 
				
			||||||
# @return: File path of depth 1
 | 
					 | 
				
			||||||
function fn(path,    n, a) {
 | 
					 | 
				
			||||||
  n = split(path, a, "/")
 | 
					 | 
				
			||||||
  return a[n-1] "/" a[n]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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) {
 | 
					 | 
				
			||||||
  summary = getcontent(summary)
 | 
					 | 
				
			||||||
  gsub("\n", " ",    summary) # This will be put on a single line
 | 
					 | 
				
			||||||
  gsub("\t", "    ", summary) # we use "\t" as delimiter
 | 
					 | 
				
			||||||
  depth = split(FILENAME, path, "/")
 | 
					 | 
				
			||||||
  collection = depth > 1 ? path[depth-1] : ""
 | 
					 | 
				
			||||||
  collection = collection in collection2label ? collection2label[collection] : collection
 | 
					 | 
				
			||||||
  return style_line "~ " collection " " gensub(/^[^0-9]*([0-9]{4})([0-9]{2}).*$/, "\\1-\\2", "1", start) " " summary OFF
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# AWK program
 | 
					 | 
				
			||||||
BEGIN {
 | 
					 | 
				
			||||||
  FS="[:;=]"
 | 
					 | 
				
			||||||
  OFS="\t"
 | 
					 | 
				
			||||||
  split(collection_labels, mapping, ";")
 | 
					 | 
				
			||||||
  for (map in mapping)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    split(mapping[map], m, "=")
 | 
					 | 
				
			||||||
    collection2label[m[1]] = m[2]
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  # Colors
 | 
					 | 
				
			||||||
  OFF = "\033[m"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
BEGINFILE             { inside = 0; rs = 0; dur = 0; summary = ""; start = "ERROR"; end = "ERROR" }
 | 
					 | 
				
			||||||
                      { gsub("\r", "") }
 | 
					 | 
				
			||||||
/^END:VEVENT/         { print "~", start, dur ? start " " end : end, title(start, summary), fn(FILENAME); nextfile }
 | 
					 | 
				
			||||||
/^DTSTART/ && inside  { start = parse_dt(getparam($0), getcontent($0)) }
 | 
					 | 
				
			||||||
/^DTEND/ && inside    { end = parse_dt(getparam($0), getcontent($0)) }
 | 
					 | 
				
			||||||
/^DURATION/ && inside { end = parse_duration($NF); dur = 1 }
 | 
					 | 
				
			||||||
/^[^ ]/ && rs         { rs = 0 }
 | 
					 | 
				
			||||||
/^ / && rs            { summary = summary substr($0, 2) }
 | 
					 | 
				
			||||||
/^SUMMARY/ && inside  { rs = 1; summary = $0 }
 | 
					 | 
				
			||||||
/^BEGIN:VEVENT/       { inside = 1 }
 | 
					 | 
				
			||||||
@@ -1,35 +0,0 @@
 | 
				
			|||||||
## src/awk/attach.awk
 | 
					 | 
				
			||||||
## Prepend attachment to iCalendar file.
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## @assign file: Path to base64-encoded content
 | 
					 | 
				
			||||||
## @assign mime: Mime
 | 
					 | 
				
			||||||
## @assign filename: Original filename
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Functions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Write attachment
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @local variables: line, aline
 | 
					 | 
				
			||||||
function write_attachment(    line, aline, fl) {
 | 
					 | 
				
			||||||
  line = "ATTACH;ENCODING=BASE64;VALUE=BINARY;FMTTYPE="mime";FILENAME="filename":"
 | 
					 | 
				
			||||||
  fl = 1
 | 
					 | 
				
			||||||
  while (getline aline <file) {
 | 
					 | 
				
			||||||
    line = line aline
 | 
					 | 
				
			||||||
    if (fl && length(line) >= 73) {
 | 
					 | 
				
			||||||
      print substr(line, 1, 73)
 | 
					 | 
				
			||||||
      line = substr(line, 74)
 | 
					 | 
				
			||||||
      fl = 0
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    while (length(line) >= 72) {
 | 
					 | 
				
			||||||
      print " "substr(line, 1, 72)
 | 
					 | 
				
			||||||
      line = substr(line, 73)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (line)
 | 
					 | 
				
			||||||
    print " "line
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# AWK program
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/^END:VEVENT$/ { write_attachment() }
 | 
					 | 
				
			||||||
{ print }
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
BEGIN                       { FS="[:;]" }
 | 
					 | 
				
			||||||
/^END:VEVENT$/              { ins = 0; exit }
 | 
					 | 
				
			||||||
/^[^ ]/ && a                { a = 0 }
 | 
					 | 
				
			||||||
/^ / && a && p              { print substr($0, 2); }
 | 
					 | 
				
			||||||
/^ / && a && !p             { if (index($0, ":")) { p = 1; print substr($0, index($0, ":")+1) } }
 | 
					 | 
				
			||||||
/^ATTACH/ && ins            { i++; }
 | 
					 | 
				
			||||||
/^ATTACH/ && ins && i == id { a = 1; if (index($0, ":")) { p = 1; print substr($0, index($0, ":")+1) } }
 | 
					 | 
				
			||||||
/^BEGIN:VEVENT$/            { ins = 1 }
 | 
					 | 
				
			||||||
@@ -1,41 +0,0 @@
 | 
				
			|||||||
# Decide if we need to read more to get all properties
 | 
					 | 
				
			||||||
# 
 | 
					 | 
				
			||||||
# @input str: strin read so far
 | 
					 | 
				
			||||||
# @return: 1 if we need more data, 0 otherwise
 | 
					 | 
				
			||||||
function cont_reading(str) {
 | 
					 | 
				
			||||||
  return index(str, ":") ? 0 : 1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Get information about attachment
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input i: Attachment index
 | 
					 | 
				
			||||||
# @input str: Attachment string (at least up to content separator `:`)
 | 
					 | 
				
			||||||
# @return: informative string
 | 
					 | 
				
			||||||
function att_info(i, str,    cnt, k, info) {
 | 
					 | 
				
			||||||
  str = substr(str, 1, index(str, ":") - 1)
 | 
					 | 
				
			||||||
  cnt = split(str, props)
 | 
					 | 
				
			||||||
  if (cnt > 1) {
 | 
					 | 
				
			||||||
    for (k=2; k<=cnt; k++) {
 | 
					 | 
				
			||||||
      pname = substr(props[k], 1, index(props[k], "=") - 1)
 | 
					 | 
				
			||||||
      pvalu = substr(props[k], index(props[k], "=") + 1)
 | 
					 | 
				
			||||||
      if (pname == "ENCODING" && pvalu = "BASE64")
 | 
					 | 
				
			||||||
        enc = "base64"
 | 
					 | 
				
			||||||
      if (pname == "FILENAME")
 | 
					 | 
				
			||||||
        fin = pvalu
 | 
					 | 
				
			||||||
      if (pname == "VALUE")
 | 
					 | 
				
			||||||
        val = pvalu
 | 
					 | 
				
			||||||
      if (pname == "FMTTYPE")
 | 
					 | 
				
			||||||
        type = pvalu
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (enc)
 | 
					 | 
				
			||||||
      info = "inline"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  print i, fin, type, enc, info
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BEGIN            { FS="[:;]"; OFS="\t" }
 | 
					 | 
				
			||||||
/^END:VEVENT$/   { ins = 0; exit }
 | 
					 | 
				
			||||||
l && !r          { att_info(i, l); l = "" }
 | 
					 | 
				
			||||||
/^ / && r        { l = l substr($0, 2); r = cont_reading($0) }
 | 
					 | 
				
			||||||
/^ATTACH/ && ins { i++; l = $0; r = cont_reading($0) }
 | 
					 | 
				
			||||||
/^BEGIN:VEVENT$/ { ins = 1 }
 | 
					 | 
				
			||||||
@@ -1,13 +0,0 @@
 | 
				
			|||||||
## src/awk/attachrm.awk
 | 
					 | 
				
			||||||
## Remove attachment from iCalendar file.
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## @assign id: Attachment number to remove
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BEGIN                       { FS="[:;]" }
 | 
					 | 
				
			||||||
/^END:VEVENT$/              { ins = 0 }
 | 
					 | 
				
			||||||
/^[^ ]/ && a                { a = 0 }
 | 
					 | 
				
			||||||
/^ / && a                   { next }
 | 
					 | 
				
			||||||
/^ATTACH/ && ins            { i++; }
 | 
					 | 
				
			||||||
/^ATTACH/ && ins && i == id { a = 1; next }
 | 
					 | 
				
			||||||
/^BEGIN:VEVENT$/            { ins = 1 }
 | 
					 | 
				
			||||||
{ print }
 | 
					 | 
				
			||||||
							
								
								
									
										18
									
								
								src/awk/cal.awk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/awk/cal.awk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					BEGIN {
 | 
				
			||||||
 | 
					  BLACK = "\033[1;30m"
 | 
				
			||||||
 | 
					  GREEN = "\033[1;32m"
 | 
				
			||||||
 | 
					  RED = "\033[1;31m"
 | 
				
			||||||
 | 
					  FAINT = "\033[2m"
 | 
				
			||||||
 | 
					  BOLD = "\033[1m"
 | 
				
			||||||
 | 
					  BG = "\033[41m"
 | 
				
			||||||
 | 
					  OFF = "\033[m"
 | 
				
			||||||
 | 
					  day = day + 0
 | 
				
			||||||
 | 
					  cur = cur + 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					NR == 1 { print GREEN $0 OFF; next }
 | 
				
			||||||
 | 
					NR == 2 { print FAINT $0 OFF; next }
 | 
				
			||||||
 | 
					{ 
 | 
				
			||||||
 | 
					  sub("\\y"cur"\\y", BG BLACK BOLD cur OFF)
 | 
				
			||||||
 | 
					  sub("\\y"day"\\y", RED BOLD day OFF)
 | 
				
			||||||
 | 
					  print
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,26 +0,0 @@
 | 
				
			|||||||
## src/awk/calannot.awk
 | 
					 | 
				
			||||||
## Annotate monthly calendar
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## @assign cur: Day-of-month to mark as `today`
 | 
					 | 
				
			||||||
## @assign day: Day-of-month to highlight
 | 
					 | 
				
			||||||
## @assign style_month: Theme to use for month
 | 
					 | 
				
			||||||
## @assign style_weekdays: Theme to use for weekdays
 | 
					 | 
				
			||||||
## @assign style_cur: Theme to use for current day 
 | 
					 | 
				
			||||||
## @assign style_highlight: Theme to use for highlighted day
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BEGIN {
 | 
					 | 
				
			||||||
  OFF = "\033[m"
 | 
					 | 
				
			||||||
  day = day + 0
 | 
					 | 
				
			||||||
  cur = cur + 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
NR == 1 { print style_month $0 OFF; next }
 | 
					 | 
				
			||||||
NR == 2 { print style_weekdays $0 OFF; next }
 | 
					 | 
				
			||||||
{ 
 | 
					 | 
				
			||||||
  if (day == cur) {
 | 
					 | 
				
			||||||
    sub("\\y"cur"\\y", style_highlight style_cur cur OFF)
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    sub("\\y"cur"\\y", style_cur cur OFF)
 | 
					 | 
				
			||||||
    sub("\\y"day"\\y", style_highlight day OFF)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  print
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,23 +0,0 @@
 | 
				
			|||||||
## src/awk/calshift.awk
 | 
					 | 
				
			||||||
## Rearrange days of a monthly output from cal (1), such that Monday is the
 | 
					 | 
				
			||||||
## first day of the week.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BEGIN { 
 | 
					 | 
				
			||||||
  ORS = ""
 | 
					 | 
				
			||||||
  W3 = "   "
 | 
					 | 
				
			||||||
  W17 = W3 W3 W3 W3 W3 "  "
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
NR == 1 { i++; print $0 "\n"; next }
 | 
					 | 
				
			||||||
NR == 2 { i++; print substr($0, 4, 17) " " substr($0, 1, 3) " \n"; next }
 | 
					 | 
				
			||||||
NR == 3 && /^ 1/ { print W17; }
 | 
					 | 
				
			||||||
NR == 3 && /^  / { print substr($0, 4, 17); next }
 | 
					 | 
				
			||||||
/[0-9]/ {
 | 
					 | 
				
			||||||
  i++
 | 
					 | 
				
			||||||
  print " " substr($0, 1, 3) " \n" substr($0, 4, 17)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
END { 
 | 
					 | 
				
			||||||
  i++
 | 
					 | 
				
			||||||
  print " " W3 " \n"
 | 
					 | 
				
			||||||
  for (i; i<8; i++)
 | 
					 | 
				
			||||||
    print " " W17 W3 " \n"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,107 +1,26 @@
 | 
				
			|||||||
## src/awk/dayview.awk
 | 
					# 11:00|13:00|1748422800|1748430000|fpath|desc...
 | 
				
			||||||
## Take as input (tab-delimited):
 | 
					# 00:00|00:00|1748296800|1748383200|fpath|desc...
 | 
				
			||||||
##   1. s (start time, as HH:MM)
 | 
					function allday(desc) {
 | 
				
			||||||
##   2. e (end time, as HH:MM)
 | 
					  return ITALIC FAINT "    (allday)    " OFF desc
 | 
				
			||||||
##   3. starttime
 | 
					 | 
				
			||||||
##   4. endtime
 | 
					 | 
				
			||||||
##   5. fpath
 | 
					 | 
				
			||||||
##   6. collection
 | 
					 | 
				
			||||||
##   7. description
 | 
					 | 
				
			||||||
##   8. status
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## filter out irrelevant lines, and generate the view of a day
 | 
					 | 
				
			||||||
## (tab-delimited), including empty hours:
 | 
					 | 
				
			||||||
##   1. start date
 | 
					 | 
				
			||||||
##   2. start time
 | 
					 | 
				
			||||||
##   3. end time
 | 
					 | 
				
			||||||
##   4. file path
 | 
					 | 
				
			||||||
##   5. collection
 | 
					 | 
				
			||||||
##   6. 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
 | 
					 | 
				
			||||||
## @assign style_allday
 | 
					 | 
				
			||||||
## @assign style_timerange
 | 
					 | 
				
			||||||
## @assign style_confirmed
 | 
					 | 
				
			||||||
## @assign style_tentative
 | 
					 | 
				
			||||||
## @assign style_cancelled
 | 
					 | 
				
			||||||
## @assign style_hour
 | 
					 | 
				
			||||||
## @assign style_emptyhour
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Functions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Set event color based on status
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# @input status: Event status, one of TENTATIVE, CONFIRMED, CANCELLED
 | 
					 | 
				
			||||||
# @return: Color modifier
 | 
					 | 
				
			||||||
function color_from_status(status) {
 | 
					 | 
				
			||||||
  return status == "CANCELLED" ? style_cancelled : status == "TENTATIVE" ? style_tentative : style_confirmed
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					function endstoday(stop, desc) {
 | 
				
			||||||
# Return line for all-day event.
 | 
					  return CYAN "      -- " stop OFF ": " desc
 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @local variables: color
 | 
					 | 
				
			||||||
# @input collection: Collection symbol
 | 
					 | 
				
			||||||
# @input desc: Event description
 | 
					 | 
				
			||||||
# @input status: Event status, one of TENTATIVE, CONFIRMED, CANCELLED
 | 
					 | 
				
			||||||
# @return: Single-line string
 | 
					 | 
				
			||||||
function allday(collection, desc, status,    color) {
 | 
					 | 
				
			||||||
  color = color_from_status(status)
 | 
					 | 
				
			||||||
  return collection " " style_allday color desc OFF
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					function slice(start, stop, desc) {
 | 
				
			||||||
# Return line for multi-day event, or event that starts at midnight, which ends today.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @local variables: color
 | 
					 | 
				
			||||||
# @input stop: Time at which the event ends
 | 
					 | 
				
			||||||
# @input collection: Collection symbol
 | 
					 | 
				
			||||||
# @input desc: Event description
 | 
					 | 
				
			||||||
# @input status: Event status, one of TENTATIVE, CONFIRMED, CANCELLED
 | 
					 | 
				
			||||||
# @return: Single-line string
 | 
					 | 
				
			||||||
function endstoday(stop, collection, desc, status) {
 | 
					 | 
				
			||||||
  color = color_from_status(status)
 | 
					 | 
				
			||||||
  return collection " " style_timerange "      → " stop ": " OFF color desc OFF
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Return line for event that starts sometime today.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @local variables: color
 | 
					 | 
				
			||||||
# @input start: Time at which the event starts
 | 
					 | 
				
			||||||
# @input stop: Time at which the event ends
 | 
					 | 
				
			||||||
# @input collection: Collection symbol
 | 
					 | 
				
			||||||
# @input desc: Event description
 | 
					 | 
				
			||||||
# @input status: Event status, one of TENTATIVE, CONFIRMED, CANCELLED
 | 
					 | 
				
			||||||
# @return: Single-line string
 | 
					 | 
				
			||||||
function slice(start, stop, collection, desc, status) {
 | 
					 | 
				
			||||||
  color = color_from_status(status)
 | 
					 | 
				
			||||||
  if (stop == "00:00")
 | 
					  if (stop == "00:00")
 | 
				
			||||||
    return collection " " style_timerange start " →      " ": " OFF color desc OFF
 | 
					    return CYAN start " --      " OFF ": " desc
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    return collection " " style_timerange start " – " stop ": " OFF color desc OFF
 | 
					    return CYAN start OFF " -- " CYAN stop OFF ": " desc
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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, "", "", "", "   " style_hou hour ":00" OFF "          " style_emptyhour
 | 
					  print hour, "", "", "", FAINT hour ":00           ----------------------" OFF
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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)
 | 
				
			||||||
  tmp = substr(start, 4, 2) == "00" ? 0 : 1
 | 
					  tmp = substr(start, 4, 2) == "00" ? 0 : 1
 | 
				
			||||||
  for (i=h; i < starth + tmp && i < dayend; i++)
 | 
					  for (i=h; i < starth + tmp; i++)
 | 
				
			||||||
    hrline(i)
 | 
					    hrline(i)
 | 
				
			||||||
  tmp = substr(stop, 4, 2) == "00" ? 0 : 1
 | 
					  tmp = substr(stop, 4, 2) == "00" ? 0 : 1
 | 
				
			||||||
  if (stoph + tmp < daystart)
 | 
					  if (stoph + tmp < daystart)
 | 
				
			||||||
@@ -109,17 +28,23 @@ function hrlines(start, stop, h,    starth, stoph, tmp, i) {
 | 
				
			|||||||
  else
 | 
					  else
 | 
				
			||||||
    return stoph + tmp
 | 
					    return stoph + tmp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
# AWK program
 | 
					 | 
				
			||||||
BEGIN {
 | 
					BEGIN {
 | 
				
			||||||
  FS = "\t"
 | 
					  FS = "|"
 | 
				
			||||||
  OFS = "\t"
 | 
					  GREEN = "\033[1;32m"
 | 
				
			||||||
 | 
					  RED = "\033[1;31m"
 | 
				
			||||||
 | 
					  WHITE = "\033[1;97m"
 | 
				
			||||||
 | 
					  CYAN = "\033[1;36m"
 | 
				
			||||||
 | 
					  ITALIC = "\033[3m"
 | 
				
			||||||
 | 
					  FAINT = "\033[2m"
 | 
				
			||||||
  OFF = "\033[m"
 | 
					  OFF = "\033[m"
 | 
				
			||||||
 | 
					  OFS = "|"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
$1 == "00:00" && $2 == "00:00" { print today, $1, $3, $4, $5, allday($6, $7, $8);        next }
 | 
					$1 == "00:00" && $2 == "00:00" { print $1, $3, $4, $5, allday($6);        next }
 | 
				
			||||||
$1 == "00:00"                  { print today, $1, $3, $4, $5, endstoday($2, $6, $7, $8); next }
 | 
					$1 == "00:00"                  { print $1, $3, $4, $5, endstoday($2, $6); next }
 | 
				
			||||||
$1 ~ /^[0-9]{2}:[0-9]{2}$/     {
 | 
					$1 ~ /^[0-9]{2}:[0-9]{2}$/     {
 | 
				
			||||||
  daystart = hrlines($1, $2, daystart)
 | 
					  daystart = hrlines($1, $2, daystart,    starth, stoph, tmp, i)
 | 
				
			||||||
  print today, $1, $3, $4, $5, slice($1, $2, $6, $7, $8)
 | 
					  print $1, $3, $4, $5, slice($1, $2, $6)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					END {
 | 
				
			||||||
 | 
					  hrlines(dayend":00", 0, daystart,    starth, stoph, tmp, i)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
END                            { hrlines(dayend":00", 0, daystart)                        }
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,18 @@
 | 
				
			|||||||
## src/awk/get.awk
 | 
					# print content of field `field`
 | 
				
			||||||
## Print content of a field of an iCalendar file.
 | 
					BEGIN                     { FS = ":"; regex = "^" field; }
 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## @assign field: Field name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@include "lib/awk/icalendar.awk"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BEGIN              { FS = ":"; regex = "^" field }
 | 
					 | 
				
			||||||
                   { gsub("\r", "") }
 | 
					 | 
				
			||||||
/^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
 | 
				
			||||||
  print getcontent(content)
 | 
					  content = substr(content, index(content, ":") + 1);
 | 
				
			||||||
 | 
					  gsub("\\\\n",    "\n", content);
 | 
				
			||||||
 | 
					  gsub("\\\\N",    "\n", content);
 | 
				
			||||||
 | 
					  gsub("\\\\,",    ",",  content);
 | 
				
			||||||
 | 
					  gsub("\\\\;",    ";",  content);
 | 
				
			||||||
 | 
					  gsub("\\\\\\\\", "\\", content);
 | 
				
			||||||
 | 
					  print content;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +0,0 @@
 | 
				
			|||||||
## src/awk/has.awk
 | 
					 | 
				
			||||||
## Decide if VEVENT file has a specific field.
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## @assign field: Field name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# AWK program
 | 
					 | 
				
			||||||
BEGIN              { FS = "[:;]" }
 | 
					 | 
				
			||||||
/^BEGIN:VEVENT$/   { ins = 1 }
 | 
					 | 
				
			||||||
/^END:VEVENT$/     { exit 1 }
 | 
					 | 
				
			||||||
ins && $1 == field { exit 0 }
 | 
					 | 
				
			||||||
							
								
								
									
										79
									
								
								src/awk/lines.awk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/awk/lines.awk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					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];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function title(start, summary) {
 | 
				
			||||||
 | 
					  summary = substr(summary, index(summary, ":") + 1);
 | 
				
			||||||
 | 
					  #gsub("\\\\n",    "\n", summary); # one-liner
 | 
				
			||||||
 | 
					  #gsub("\\\\N",    "\n", summary); # one-liner
 | 
				
			||||||
 | 
					  gsub("\\\\n",    " ",  summary);
 | 
				
			||||||
 | 
					  gsub("\\\\N",    " ",  summary);
 | 
				
			||||||
 | 
					  gsub("\\\\,",    ",",  summary);
 | 
				
			||||||
 | 
					  gsub("\\\\;",    ";",  summary);
 | 
				
			||||||
 | 
					  gsub("\\\\\\\\", "\\", summary);
 | 
				
			||||||
 | 
					  gsub("\\|", ":", summary); # we use "|" as delimiter
 | 
				
			||||||
 | 
					  depth = split(FILENAME, path, "/");
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BEGIN                 { 
 | 
				
			||||||
 | 
					  FS="[:;=]";
 | 
				
			||||||
 | 
					  OFS="|" 
 | 
				
			||||||
 | 
					  split(collection_labels, mapping, ";");
 | 
				
			||||||
 | 
					  for (map in mapping)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    split(mapping[map], m, "=");
 | 
				
			||||||
 | 
					    collection2label[m[1]] = m[2];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  # Colors
 | 
				
			||||||
 | 
					  GREEN = "\033[1;32m";
 | 
				
			||||||
 | 
					  RED = "\033[1;31m";
 | 
				
			||||||
 | 
					  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" }
 | 
				
			||||||
 | 
					/^END:VEVENT/         { print "~", start, dur ? start " " end : end, title(start, summary), 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 }
 | 
				
			||||||
 | 
					/^[^ ]/ && rs         { rs = 0 }
 | 
				
			||||||
 | 
					/^ / && rs            { summary = summary substr($0, 2); }
 | 
				
			||||||
 | 
					/^SUMMARY/ && inside  { rs = 1; summary = $0; }
 | 
				
			||||||
 | 
					/^BEGIN:VEVENT/       { inside = 1 }
 | 
				
			||||||
@@ -1,35 +1,17 @@
 | 
				
			|||||||
## src/awk/merge.awk
 | 
					BEGIN { FS="|"; i=0; dlt = -259200; spw = 604800; }
 | 
				
			||||||
## 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="\t"; OFS="\t" }
 | 
					 | 
				
			||||||
NR == FNR {
 | 
					NR == FNR {
 | 
				
			||||||
  i = i + 1
 | 
					  i = i + 1;
 | 
				
			||||||
  split($0, parts, ":")
 | 
					  from[i] = int(($1 + dlt)/ spw);
 | 
				
			||||||
  from_year[i] = parts[1]
 | 
					  getline;
 | 
				
			||||||
  from_week[i] = parts[2]
 | 
					  to[i] = int(($1 + dlt) / spw);
 | 
				
			||||||
  getline
 | 
					 | 
				
			||||||
  split($0, parts, ":")
 | 
					 | 
				
			||||||
  to_year[i] = parts[1]
 | 
					 | 
				
			||||||
  to_week[i] = parts[2]
 | 
					 | 
				
			||||||
  next
 | 
					  next
 | 
				
			||||||
} # Load start and end week numbers from first file
 | 
					} # Load start and end week numbers from first file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{ 
 | 
					{ 
 | 
				
			||||||
  year_i = from_year[FNR]
 | 
					  if (from[FNR] > to[FNR])
 | 
				
			||||||
  week_i = from_week[FNR]
 | 
					    print "FNR", FNR, ":", from[FNR],"-",to[FNR], "    ",$0;
 | 
				
			||||||
  year_end = to_year[FNR]
 | 
					  for(i=from[FNR]; i<=to[FNR]; i++) {
 | 
				
			||||||
  week_end = to_week[FNR]
 | 
					    week[i] = week[i] " " $5
 | 
				
			||||||
  while(year_i <= year_end && (year_i < year_end || week_i <= week_end)) {
 | 
					 | 
				
			||||||
    label = year_i ":" week_i ":"
 | 
					 | 
				
			||||||
    week[label] = week[label] ? week[label] " " $5 : $5
 | 
					 | 
				
			||||||
    week_i++
 | 
					 | 
				
			||||||
    if (week_i > 53) {
 | 
					 | 
				
			||||||
      week_i = 1
 | 
					 | 
				
			||||||
      year_i++
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
END { for (label in week) print label, week[label] }
 | 
					END { for (i in week) print i week[i]; }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										116
									
								
								src/awk/new.awk
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								src/awk/new.awk
									
									
									
									
									
								
							@@ -1,58 +1,63 @@
 | 
				
			|||||||
## src/awk/new.awk
 | 
					function escape(str)
 | 
				
			||||||
## Generate iCalendar file from markdown description.
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## @assign uid: UID to use
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@include "lib/awk/icalendar.awk"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# AWK program
 | 
					 | 
				
			||||||
BEGIN { 
 | 
					 | 
				
			||||||
  FS=":"
 | 
					 | 
				
			||||||
  zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
readdesc { desc = desc ? desc "\\n" escape($0) : escape($0); next }
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""
 | 
					  gsub("\\\\", "\\\\", str);
 | 
				
			||||||
 | 
					  gsub(";",  "\\\\;",    str);
 | 
				
			||||||
 | 
					  gsub(",",  "\\\\,",    str);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function print_fold(nameparam, content,    i, s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  i = 74 - length(nameparam);
 | 
				
			||||||
 | 
					  s = substr(content, 1, i);
 | 
				
			||||||
 | 
					  print nameparam s;
 | 
				
			||||||
 | 
					  s = substr(content, i+1, 73);
 | 
				
			||||||
 | 
					  i = i + 73;
 | 
				
			||||||
 | 
					  while (s)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    print " " s;
 | 
				
			||||||
 | 
					    s = substr(content, i+1, 73);
 | 
				
			||||||
 | 
					    i = i + 73;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BEGIN { 
 | 
				
			||||||
 | 
					  FS=":"; 
 | 
				
			||||||
 | 
					  zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					desc { desc = desc "\\n" $0; next; }
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
  location = substr($0, 1, 2) == "@ " ? escape(substr($0, 3)) : ""
 | 
					  summary = substr($0, 1, 2) == "# " ? substr($0, 3) : ""
 | 
				
			||||||
  if (location) getline
 | 
					 | 
				
			||||||
  summary = substr($0, 1, 2) == "# " ? escape(substr($0, 3)) : ""
 | 
					 | 
				
			||||||
  if (!summary)
 | 
					  if (!summary)
 | 
				
			||||||
    exit 1
 | 
					    exit 1
 | 
				
			||||||
  getline # This line should be empty
 | 
					  getline # This line should be empty
 | 
				
			||||||
  if ($0 != "")
 | 
					  getline # First line of description
 | 
				
			||||||
    exit 1
 | 
					  desc = $0;
 | 
				
			||||||
  readdesc = 1
 | 
					  next;
 | 
				
			||||||
  next
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
END {
 | 
					END {
 | 
				
			||||||
  # Sanitize input
 | 
					  # Sanitize input
 | 
				
			||||||
  # If nanoseconds are not 0, then we assume user entered "tomorrow" or
 | 
					  # If nanoseconds are not 0, then we assume user enterd "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.
 | 
				
			||||||
  # Similarly, if the time is 00:00, we make this a date, as opposed to a
 | 
					 | 
				
			||||||
  # date-time entry.
 | 
					 | 
				
			||||||
  gsub("\"", "\\\"", from)
 | 
					  gsub("\"", "\\\"", from)
 | 
				
			||||||
  cmd = "date -d \"" from "\" +\"%N\""
 | 
					  cmd = "date -d \"" from "\" +\"%N\"";
 | 
				
			||||||
  cmd | getline n
 | 
					 | 
				
			||||||
  close(cmd)
 | 
					 | 
				
			||||||
  n = n + 0
 | 
					 | 
				
			||||||
  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 (t == 0) {
 | 
				
			||||||
    from_type = "DATE"
 | 
					 | 
				
			||||||
    cmd = "date -d \"" from "\" +\"%Y%m%d\""
 | 
					 | 
				
			||||||
  } 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"
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    from_type = "DATE"
 | 
				
			||||||
 | 
					    cmd = "date -d \"" from "\" +\"%Y%m%d\"";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  suc = cmd | getline from
 | 
					  suc = cmd | getline from
 | 
				
			||||||
  close(cmd)
 | 
					  close(cmd)
 | 
				
			||||||
@@ -61,45 +66,42 @@ END {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  #
 | 
					  #
 | 
				
			||||||
  gsub("\"", "\\\"", to)
 | 
					  gsub("\"", "\\\"", to)
 | 
				
			||||||
  cmd = "date -d \"" to "\" +\"%N\""
 | 
					  cmd = "date -d \"" to "\" +\"%N\"";
 | 
				
			||||||
  cmd | getline n
 | 
					 | 
				
			||||||
  close(cmd)
 | 
					 | 
				
			||||||
  n = n + 0
 | 
					 | 
				
			||||||
  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 (t == 0) {
 | 
				
			||||||
    to_type = "DATE"
 | 
					 | 
				
			||||||
    cmd = "date -d \"" to "\" +\"%Y%m%d\""
 | 
					 | 
				
			||||||
  } 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"
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    to_type = "DATE"
 | 
				
			||||||
 | 
					    cmd = "date -d \"" to "\" +\"%Y%m%d\"";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  suc = cmd | getline to
 | 
					  suc = cmd | getline to
 | 
				
			||||||
  close(cmd)
 | 
					  close(cmd)
 | 
				
			||||||
  if (suc != 1) {
 | 
					  if (suc != 1) {
 | 
				
			||||||
    exit 1
 | 
					    exit 1
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  escape(summary);
 | 
				
			||||||
 | 
					  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:CONFIRMED"
 | 
					  print "STATUS:FINAL";
 | 
				
			||||||
  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)
 | 
					  if (summary)    print_fold("SUMMARY:",     summary,       i, s);
 | 
				
			||||||
  if (desc)       print_fold("DESCRIPTION:", desc)
 | 
					  if (desc)       print_fold("DESCRIPTION:", desc,          i, s);
 | 
				
			||||||
  if (location)   print_fold("LOCATION:",    location)
 | 
					 | 
				
			||||||
  print "END:VEVENT"
 | 
					  print "END:VEVENT"
 | 
				
			||||||
  print "END:VCALENDAR"
 | 
					  print "END:VCALENDAR"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +1,46 @@
 | 
				
			|||||||
## src/awk/parse.awk
 | 
					function parse(    dt) {
 | 
				
			||||||
## Parse iCalendar file and print its key aspects:
 | 
					  # Get timezone information
 | 
				
			||||||
## ```
 | 
					  dt = "";
 | 
				
			||||||
## <start> <end> <fpath> <collection> <status> <summary>
 | 
					  for (i=2; i<NF-1; i+=2) {
 | 
				
			||||||
## ```.
 | 
					    if ($i == "TZID") {
 | 
				
			||||||
## The output is space delimited.
 | 
					      dt = "TZ=\"" $(i+1) "\" ";
 | 
				
			||||||
## Summary may contain spaces, but it's the last in the list.
 | 
					      break;
 | 
				
			||||||
##
 | 
					    }
 | 
				
			||||||
## @assign collection_labels: See configuration of the current program.
 | 
					  }
 | 
				
			||||||
 | 
					  # 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@include "lib/awk/icalendar.awk"
 | 
					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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Print string of parsed data.
 | 
					function print_data(start, dur, end, summary,    cmd, collection) {
 | 
				
			||||||
#
 | 
					  summary = substr(summary, index(summary, ":") + 1);
 | 
				
			||||||
# @local variables: cmd, collection, depth, path
 | 
					  gsub("\\\\n",    " ", summary); # one-liner
 | 
				
			||||||
# @input start: Start time of event
 | 
					  gsub("\\\\N",    " ", summary); # one-liner
 | 
				
			||||||
# @input dur: Boolean that indicates that `end` specifies a duration
 | 
					  gsub("\\\\,",    ",",  summary);
 | 
				
			||||||
# @input end: End time of event, or event duration (see `dur`)
 | 
					  gsub("\\\\;",    ";",  summary);
 | 
				
			||||||
# @input summary: Content of SUMMARY field of the event
 | 
					  gsub("\\\\\\\\", "\\", summary);
 | 
				
			||||||
function print_data(start, dur, end, summary,    cmd, collection, depth, path) {
 | 
					  depth = split(FILENAME, path, "/");
 | 
				
			||||||
  summary = getcontent(summary)
 | 
					 | 
				
			||||||
  gsub("\n", " ",    summary) # This will be put on a single line
 | 
					 | 
				
			||||||
  gsub("\t", "    ", summary) # Generally, we use tab as delimiter.
 | 
					 | 
				
			||||||
  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\""
 | 
				
			||||||
@@ -33,27 +49,23 @@ function print_data(start, dur, end, summary,    cmd, collection, depth, path) {
 | 
				
			|||||||
  cmd = "date -d '" end "' +\"%s\""
 | 
					  cmd = "date -d '" end "' +\"%s\""
 | 
				
			||||||
  cmd | getline end
 | 
					  cmd | getline end
 | 
				
			||||||
  close(cmd)
 | 
					  close(cmd)
 | 
				
			||||||
  status = status ? status : "CONFIRMED"
 | 
					  print start, end, fpath, collection, summary
 | 
				
			||||||
  print start, end, fpath, collection, status, 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];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
                        { gsub("\r", "") }
 | 
					/^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_dt(getparam($0), getcontent($0)) }
 | 
					/^DTEND/ && inside    { end = parse(    dt) }
 | 
				
			||||||
/^DTEND/ && inside      { end = parse_dt(getparam($0), getcontent($0)) }
 | 
					/^DURATION/ && inside { end = parse_duration(    dt, dta, i, n, a, seps); dur = 1 }
 | 
				
			||||||
/^DURATION/ && inside   { end = parse_duration($NF); dur = 1 }
 | 
					 | 
				
			||||||
/^STATUS/ && inside     { status = $NF }
 | 
					 | 
				
			||||||
/^[^ ]/ && 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 }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +0,0 @@
 | 
				
			|||||||
## 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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@include "lib/awk/icalendar.awk"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BEGIN                 { FS = "[:;]"; zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1) }
 | 
					 | 
				
			||||||
/^BEGIN:VEVENT$/      { inside = 1 }
 | 
					 | 
				
			||||||
/^END:VEVENT$/        {
 | 
					 | 
				
			||||||
  inside = 0
 | 
					 | 
				
			||||||
  if (!duplic)
 | 
					 | 
				
			||||||
    print field ":" escape(value)
 | 
					 | 
				
			||||||
  seq = seq ? seq + 1 : 1
 | 
					 | 
				
			||||||
  print "SEQUENCE:" seq
 | 
					 | 
				
			||||||
  print "LAST-MODIFIED:" zulu
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
$1 == field && inside { con = 1; duplic = 1; print field ":" escape(value); next }
 | 
					 | 
				
			||||||
$1 == field && duplic { con = 1; next }
 | 
					 | 
				
			||||||
/^ / && con           { next }
 | 
					 | 
				
			||||||
/^[^ ]/ && con        { con = 0 }
 | 
					 | 
				
			||||||
/^SEQUENCE/ && inside { seq = $2; next } # store sequence number and skip
 | 
					 | 
				
			||||||
/^LAST-MODIFIED/ && inside { next }
 | 
					 | 
				
			||||||
{ print }
 | 
					 | 
				
			||||||
@@ -1,103 +1,101 @@
 | 
				
			|||||||
## src/awk/update.awk
 | 
					function getcontent(content_line, prop)
 | 
				
			||||||
## Update iCalendar file from markdown file.
 | 
					{
 | 
				
			||||||
 | 
					  return substr(content_line[prop], index(content_line[prop], ":") + 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@include "lib/awk/icalendar.awk"
 | 
					function escape(str)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  gsub("\\\\", "\\\\", str);
 | 
				
			||||||
 | 
					  gsub(";",  "\\\\;",    str);
 | 
				
			||||||
 | 
					  gsub(",",  "\\\\,",    str);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function print_fold(nameparam, content,    i, s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  i = 74 - length(nameparam);
 | 
				
			||||||
 | 
					  s = substr(content, 1, i);
 | 
				
			||||||
 | 
					  print nameparam s;
 | 
				
			||||||
 | 
					  s = substr(content, i+1, 73);
 | 
				
			||||||
 | 
					  i = i + 73;
 | 
				
			||||||
 | 
					  while (s)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    print " " s;
 | 
				
			||||||
 | 
					    s = substr(content, i+1, 73);
 | 
				
			||||||
 | 
					    i = i + 73;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 entered "tomorrow" or
 | 
					    # If nanoseconds are not 0, then we assume user enterd "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)
 | 
					    from = from ? from : "now"
 | 
				
			||||||
    cmd = "date -d \"" from "\" +\"%N\""
 | 
					    cmd = "date -d \"" from "\" +\"%N\"";
 | 
				
			||||||
    cmd | getline n
 | 
					 | 
				
			||||||
    close(cmd)
 | 
					 | 
				
			||||||
    n = n + 0
 | 
					 | 
				
			||||||
    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 (t == 0) {
 | 
				
			||||||
      from_type = "DATE"
 | 
					 | 
				
			||||||
      cmd = "date -d \"" from "\" +\"%Y%m%d\""
 | 
					 | 
				
			||||||
    } 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"
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      from_type = "DATE"
 | 
				
			||||||
 | 
					      cmd = "date -d \"" from "\" +\"%Y%m%d\"";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    suc = cmd | getline from
 | 
					    cmd | getline from
 | 
				
			||||||
    close(cmd)
 | 
					    close(cmd)
 | 
				
			||||||
    if (suc != 1) {
 | 
					 | 
				
			||||||
      exit 1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    gsub("\"", "\\\"", to)
 | 
					    to = to ? to : "now"
 | 
				
			||||||
    cmd = "date -d \"" to "\" +\"%N\""
 | 
					    cmd = "date -d \"" to "\" +\"%N\"";
 | 
				
			||||||
    cmd | getline n
 | 
					 | 
				
			||||||
    close(cmd)
 | 
					 | 
				
			||||||
    n = n + 0
 | 
					 | 
				
			||||||
    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 (t == 0) {
 | 
				
			||||||
      to_type = "DATE"
 | 
					 | 
				
			||||||
      cmd = "date -d \"" to "\" +\"%Y%m%d\""
 | 
					 | 
				
			||||||
    } 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"
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      to_type = "DATE"
 | 
				
			||||||
 | 
					      cmd = "date -d \"" to "\" +\"%Y%m%d\"";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    suc = cmd | getline to
 | 
					    cmd | getline to
 | 
				
			||||||
    close(cmd)
 | 
					    close(cmd)
 | 
				
			||||||
    if (suc != 1) {
 | 
					 | 
				
			||||||
      exit 1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  escape(summary);
 | 
				
			||||||
 | 
					  escape(desc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NR == FNR && readdesc { desc = desc ? desc "\\n" escape($0) : escape($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)
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
  getline
 | 
					  getline
 | 
				
			||||||
  to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : ""
 | 
					  to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : "";
 | 
				
			||||||
  if (!to)
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
  getline
 | 
					  getline
 | 
				
			||||||
  location = substr($0, 1, 2) == "@ " ? escape(substr($0, 3)) : ""
 | 
					  summary = substr($0, 1, 2) == "# " ? substr($0, 3) : ""
 | 
				
			||||||
  if (location) getline
 | 
					 | 
				
			||||||
  summary = substr($0, 1, 2) == "# " ? escape(substr($0, 3)) : ""
 | 
					 | 
				
			||||||
  if (!summary)
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
  getline # This line should be empty
 | 
					  getline # This line should be empty
 | 
				
			||||||
  if ($0 != "")
 | 
					  getline # First line of description
 | 
				
			||||||
    exit 1
 | 
					  desc = $0;
 | 
				
			||||||
  readdesc = 1
 | 
					  next;
 | 
				
			||||||
  next
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
{ gsub("\r", "") }
 | 
					
 | 
				
			||||||
 | 
					/^BEGIN:VEVENT$/                                              { inside = 1; print; next }
 | 
				
			||||||
 | 
					/^X-ALT-DESC/ && inside                                       { next } # drop this alternative description
 | 
				
			||||||
 | 
					/^ / && inside                                                { next } # drop this folded line (the only content with folded lines will be updated)
 | 
				
			||||||
 | 
					/^(DTSTART|DTEND|SUMMARY|CATEGORIES|DESCRIPTION|LAST-MODIFIED)/ && inside { next } # skip for now, we will write updated fields at the end
 | 
				
			||||||
 | 
					/^SEQUENCE/ && inside                                         { seq = $2; next } # store sequence number and skip
 | 
				
			||||||
/^END:VEVENT$/ {
 | 
					/^END:VEVENT$/ {
 | 
				
			||||||
  seq = seq ? seq + 1 : 1
 | 
					  seq = seq ? seq + 1 : 1
 | 
				
			||||||
  print "SEQUENCE:" seq
 | 
					  print "SEQUENCE:" seq
 | 
				
			||||||
  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)
 | 
					  print_fold("SUMMARY:",     summary,       i, s)
 | 
				
			||||||
  print_fold("DESCRIPTION:", desc)
 | 
					  print_fold("DESCRIPTION:", desc,          i, s)
 | 
				
			||||||
  print_fold("LOCATION:",    location)
 | 
					 | 
				
			||||||
  inside = ""
 | 
					  inside = ""
 | 
				
			||||||
  skipf = 0
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/^BEGIN:VEVENT$/           { inside = 1 }
 | 
					 | 
				
			||||||
/^ / && skipf              { next } # drop this folded line
 | 
					 | 
				
			||||||
/^[^ ]/ && skipf           { skipf = 0 }
 | 
					 | 
				
			||||||
/^(DTSTART|DTEND|SUMMARY|LOCATION|CATEGORIES|DESCRIPTION|LAST-MODIFIED)/ && inside { skipf = 1; next } # skip for now, we will write updated fields at the end
 | 
					 | 
				
			||||||
/^X-ALT-DESC/ && inside    { skipf = 1; next } # skip
 | 
					 | 
				
			||||||
/^SEQUENCE/ && inside      { seq = $2; next } # store sequence number and skip
 | 
					 | 
				
			||||||
{ print }
 | 
					{ print }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										44
									
								
								src/awk/weeks.awk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/awk/weeks.awk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					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 }
 | 
				
			||||||
@@ -1,39 +1,23 @@
 | 
				
			|||||||
## src/awk/weekview.awk
 | 
					function c() {
 | 
				
			||||||
## Print view of all appointments of the current week.
 | 
					  return CYAN substr($0, index($0, ">") + 1) OFF "  " RED "/" OFF
 | 
				
			||||||
## Generates view from
 | 
					 | 
				
			||||||
##   printf "%s\t%s\t%s\t%s\n" "$i" "$s" "$e" "$description"
 | 
					 | 
				
			||||||
##
 | 
					 | 
				
			||||||
## @assign startofweek: Date of first day in the week
 | 
					 | 
				
			||||||
## @assign style_day: Style for dates
 | 
					 | 
				
			||||||
## @assign style_event_delim: Event delimiter
 | 
					 | 
				
			||||||
## @assign style_summary: Style for summary lines
 | 
					 | 
				
			||||||
## @assign style_time: Style for times
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Functions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Compose line that will display a day in the week.
 | 
					 | 
				
			||||||
# 
 | 
					 | 
				
			||||||
# @input desc: String with a description of the event
 | 
					 | 
				
			||||||
# @return: Single-line string
 | 
					 | 
				
			||||||
function c(desc) {
 | 
					 | 
				
			||||||
  return style_summary desc OFF "  " style_event_delim
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
# AWK program
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BEGIN {
 | 
					BEGIN {
 | 
				
			||||||
  FS = "\t"
 | 
					  GREEN = "\033[1;32m";
 | 
				
			||||||
  OFS = "\t"
 | 
					  RED = "\033[1;31m";
 | 
				
			||||||
  OFF = "\033[m"
 | 
					  WHITE = "\033[1;97m";
 | 
				
			||||||
 | 
					  CYAN = "\033[1;36m";
 | 
				
			||||||
 | 
					  FAINT = "\033[2m";
 | 
				
			||||||
 | 
					  OFF = "\033[m";
 | 
				
			||||||
 | 
					  OFS = "|"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
$2 == "00:00" && $3 == "00:00" { dayline = dayline " " c($4); next }
 | 
					/^[0-7] 00:00 -- 00:00/ { dayline = dayline " " c(); next }
 | 
				
			||||||
$2 == "00:00"                  { dayline = dayline style_time " → " $3 OFF " " c($4); next }
 | 
					/^[0-7] 00:00 -- / { dayline = dayline " <--" $4 " " c(); next }
 | 
				
			||||||
$3 == "00:00"                  { dayline = dayline style_time " " $2 " → " OFF c($4); next }
 | 
					/^[0-7] [0-9]{2}:[0-9]{2} -- 00:00/ { dayline = dayline " " $2 "--> " c(); next }
 | 
				
			||||||
NF == 4                        { dayline = dayline style_time " " $2 " – " $3 OFF " " c($4); next }
 | 
					/^[0-7] [0-9]{2}:[0-9]{2} -- [0-9]{2}:[0-9]{2}/ { dayline = dayline " " $2 " - " $4 " " c(); next }
 | 
				
			||||||
NF == 1 && dayline             { print "+", startofweek " +" $1-1 " days", "", dayline }
 | 
					/^[0-7]$/ && dayline { print "+", startofweek " +" $0-1 " days", "", dayline; }
 | 
				
			||||||
NF == 1 {
 | 
					/^[0-7]$/ {
 | 
				
			||||||
  cmd = "date -d '" startofweek " +" $1 " 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 = style_day dayline ":   " OFF
 | 
					  dayline = GREEN dayline ":   " OFF
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,121 +0,0 @@
 | 
				
			|||||||
# Escape string to be used as content in iCalendar files.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input str: String to escape
 | 
					 | 
				
			||||||
# @return: Escaped string
 | 
					 | 
				
			||||||
function escape(str)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  gsub("\\\\", "\\\\", str)
 | 
					 | 
				
			||||||
  gsub("\\n",  "\\n",  str)
 | 
					 | 
				
			||||||
  gsub(";",    "\\;",  str)
 | 
					 | 
				
			||||||
  gsub(",",    "\\,",  str)
 | 
					 | 
				
			||||||
  return str
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  i = 74 - length(nameparam)
 | 
					 | 
				
			||||||
  s = substr(content, 1, i)
 | 
					 | 
				
			||||||
  print nameparam s
 | 
					 | 
				
			||||||
  s = substr(content, i+1, 73)
 | 
					 | 
				
			||||||
  i = i + 73
 | 
					 | 
				
			||||||
  while (s)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    print " " s
 | 
					 | 
				
			||||||
    s = substr(content, i+1, 73)
 | 
					 | 
				
			||||||
    i = i + 73
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Unescape string
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @local variables: i, c, c2, res
 | 
					 | 
				
			||||||
# @input str: String
 | 
					 | 
				
			||||||
# @return: Unescaped string
 | 
					 | 
				
			||||||
function unescape(str,    i, c, c2, res) {
 | 
					 | 
				
			||||||
  for(i = 1; i <= length(str); i++) {
 | 
					 | 
				
			||||||
    c = substr(str, i, 1)
 | 
					 | 
				
			||||||
    if (c != "\\") {
 | 
					 | 
				
			||||||
      res = res c
 | 
					 | 
				
			||||||
      continue
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    i++
 | 
					 | 
				
			||||||
    c2 = substr(str, i, 1)
 | 
					 | 
				
			||||||
    if (c2 == "n" || c2 == "N") {
 | 
					 | 
				
			||||||
      res = res "\n"
 | 
					 | 
				
			||||||
      continue
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    # Alternatively, c2 is "\\" or "," or ";". In each case, append res with
 | 
					 | 
				
			||||||
    # c2. If the strings has been escaped correctly, then the character c2
 | 
					 | 
				
			||||||
    # cannot be anything else. To be fail-safe, simply append res with c2.
 | 
					 | 
				
			||||||
    res = res c2
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return res
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Isolate parameter part of an iCalendar line.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input str: String
 | 
					 | 
				
			||||||
# @return: Parameter part
 | 
					 | 
				
			||||||
function getparam(str,    i) {
 | 
					 | 
				
			||||||
  i = index(str, ";")
 | 
					 | 
				
			||||||
  if (!i)
 | 
					 | 
				
			||||||
    return ""
 | 
					 | 
				
			||||||
  return substr(str, i + 1, index(str, ":") - i)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Isolate content part of an iCalendar line, and unescape.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input str: String
 | 
					 | 
				
			||||||
# @return: Unescaped content part
 | 
					 | 
				
			||||||
function getcontent(str) {
 | 
					 | 
				
			||||||
  return unescape(substr(str, index(str, ":") + 1))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Time-zone aware parsing of DTSTART or DTEND entries.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @local variables: tz
 | 
					 | 
				
			||||||
# @input dt_param: iCalendar DTSTART or DTEND parameter string
 | 
					 | 
				
			||||||
# @input dt_content: iCalendar DTSTART or DTEND content string
 | 
					 | 
				
			||||||
# @return: date or date-time string that can be used in date (1)
 | 
					 | 
				
			||||||
function parse_dt(dt_param, dt_content,    tz, a, i, k) {
 | 
					 | 
				
			||||||
  if (dt_param) {
 | 
					 | 
				
			||||||
    split(dt_param, a, ";")
 | 
					 | 
				
			||||||
    for (i in a) {
 | 
					 | 
				
			||||||
      k = index(a[i], "=")
 | 
					 | 
				
			||||||
      if (substr(a[i], 1, k-1) == "TZID") {
 | 
					 | 
				
			||||||
        tz = "TZ=\"" substr(a[i], k + 1) "\" "
 | 
					 | 
				
			||||||
        break
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  # Get date/date-time
 | 
					 | 
				
			||||||
  return length(dt_content) == 8 ?
 | 
					 | 
				
			||||||
    dt dt_content :
 | 
					 | 
				
			||||||
    dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", dt_content)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Map iCalendar duration specification into the format to be used in date (1).
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @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++) {
 | 
					 | 
				
			||||||
    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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										939
									
								
								src/main.sh
									
									
									
									
									
								
							
							
						
						
									
										939
									
								
								src/main.sh
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,121 +0,0 @@
 | 
				
			|||||||
# AWK scripts
 | 
					 | 
				
			||||||
# - AWK_APPROX:   Generate approximate data of all files
 | 
					 | 
				
			||||||
# - AWK_CALSHIFT: Shift calendar to start weeks on Mondays
 | 
					 | 
				
			||||||
# - AWK_CALANNOT: Annotate calendar
 | 
					 | 
				
			||||||
# - AWK_DAYVIEW:  Generate view of the day
 | 
					 | 
				
			||||||
# - AWK_GET:      Print field of iCalendar file
 | 
					 | 
				
			||||||
# - AWK_MERGE:    Generate list of weeks with associated iCalendar files
 | 
					 | 
				
			||||||
# - AWK_NEW:      Make new iCalendar file
 | 
					 | 
				
			||||||
# - AWK_PARSE:    Timezone aware parsing of iCalendar file for day view
 | 
					 | 
				
			||||||
# - AWK_SET:      Set value of specific field in iCalendar file
 | 
					 | 
				
			||||||
# - AWK_UPDATE:   Update iCalendar file
 | 
					 | 
				
			||||||
# - AWK_WEEKVIEW: Generate view of the week
 | 
					 | 
				
			||||||
# - AWK_ATTACHLS: List attachments
 | 
					 | 
				
			||||||
# - AWK_ATTACHDD: Store attachment
 | 
					 | 
				
			||||||
# - AWK_ATTACHRM: Remove attachment
 | 
					 | 
				
			||||||
# - AWK_ATTACH:   Add attachment
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_APPROX=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/approx.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_APPROX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_MERGE=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/merge.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_MERGE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_PARSE=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/parse.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_PARSE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_WEEKVIEW=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/weekview.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_WEEKVIEW
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_DAYVIEW=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/dayview.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_DAYVIEW
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_GET=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/get.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_GET
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_UPDATE=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/update.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_UPDATE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_NEW=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/new.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_NEW
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_CALSHIFT=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/calshift.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_CALSHIFT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_CALANNOT=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/calannot.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_CALANNOT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_SET=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/set.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_SET
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_ATTACHLS=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/attachls.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_ATTACHLS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_ATTACHDD=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/attachdd.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_ATTACHDD
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_ATTACHRM=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/attachrm.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_ATTACHRM
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AWK_ATTACH=$(
 | 
					 | 
				
			||||||
  cat <<'EOF'
 | 
					 | 
				
			||||||
@@include awk/attach.awk
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
export AWK_ATTACH
 | 
					 | 
				
			||||||
@@ -1,129 +0,0 @@
 | 
				
			|||||||
# Extra command-line options
 | 
					 | 
				
			||||||
# - --import-ni
 | 
					 | 
				
			||||||
# - --import
 | 
					 | 
				
			||||||
# - --git
 | 
					 | 
				
			||||||
# - --git-init
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Import iCalendar file noninteractively
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $2: Absolute path to iCalendar file
 | 
					 | 
				
			||||||
# @input $3: Collection
 | 
					 | 
				
			||||||
# @return: On success, returns 0, otherwise 1
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--import-ni" ]; then
 | 
					 | 
				
			||||||
  shift
 | 
					 | 
				
			||||||
  file="${1:-}"
 | 
					 | 
				
			||||||
  collection="${2:-}"
 | 
					 | 
				
			||||||
  if [ ! -f "$file" ]; then
 | 
					 | 
				
			||||||
    err "File \"$file\" does not exist"
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  for c in $(echo "$COLLECTION_LABELS" | sed "s|=[^;]*;| |g"); do
 | 
					 | 
				
			||||||
    if [ "$collection" = "$c" ]; then
 | 
					 | 
				
			||||||
      cexists="yes"
 | 
					 | 
				
			||||||
      break
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
  if [ -n "${cexists:-}" ] && [ -d "$ROOT/$collection" ]; then
 | 
					 | 
				
			||||||
    __import_to_collection "$file" "$collection"
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    err "Collection \"$collection\" does not exist"
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Import iCalendar file.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $2: Absolute path to iCalendar file
 | 
					 | 
				
			||||||
# @return: On success, returns 0, otherwise 1
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--import" ]; then
 | 
					 | 
				
			||||||
  shift
 | 
					 | 
				
			||||||
  file="${1:-}"
 | 
					 | 
				
			||||||
  if [ ! -f "$file" ]; then
 | 
					 | 
				
			||||||
    err "File \"$file\" does not exist"
 | 
					 | 
				
			||||||
    return 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  line=$(awk \
 | 
					 | 
				
			||||||
    -v collection_labels="$COLLECTION_LABELS" \
 | 
					 | 
				
			||||||
    "$AWK_PARSE" "$file")
 | 
					 | 
				
			||||||
  set -- $line
 | 
					 | 
				
			||||||
  startsec="${1:-}"
 | 
					 | 
				
			||||||
  endsec="${2:-}"
 | 
					 | 
				
			||||||
  if [ -z "$line" ] || [ -z "$startsec" ] || [ -z "$endsec" ]; then
 | 
					 | 
				
			||||||
    err "File \"$file\" does not look like an iCalendar file containing an event"
 | 
					 | 
				
			||||||
    return 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  start=$(__datetime_human_machine "$startsec")
 | 
					 | 
				
			||||||
  end=$(__datetime_human_machine "$endsec")
 | 
					 | 
				
			||||||
  location=$(awk -v field="LOCATION" "$AWK_GET" "$file")
 | 
					 | 
				
			||||||
  summary=$(awk -v field="SUMMARY" "$AWK_GET" "$file")
 | 
					 | 
				
			||||||
  description=$(awk -v field="DESCRIPTION" "$AWK_GET" "$file")
 | 
					 | 
				
			||||||
  filetmp=$(mktemp --suffix='.md')
 | 
					 | 
				
			||||||
  (
 | 
					 | 
				
			||||||
    echo "::: |> $start"
 | 
					 | 
				
			||||||
    echo "::: <| $end"
 | 
					 | 
				
			||||||
  ) >"$filetmp"
 | 
					 | 
				
			||||||
  if [ -n "$location" ]; then
 | 
					 | 
				
			||||||
    echo "@ $location" >>"$filetmp"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  (
 | 
					 | 
				
			||||||
    echo "# $summary"
 | 
					 | 
				
			||||||
    echo ""
 | 
					 | 
				
			||||||
    echo "$description"
 | 
					 | 
				
			||||||
  ) >>"$filetmp"
 | 
					 | 
				
			||||||
  $CAT "$filetmp" >/dev/tty
 | 
					 | 
				
			||||||
  while true; do
 | 
					 | 
				
			||||||
    printf "Do you want to import this entry? (yes/no): " >/dev/tty
 | 
					 | 
				
			||||||
    read -r yn
 | 
					 | 
				
			||||||
    case $yn in
 | 
					 | 
				
			||||||
    "yes")
 | 
					 | 
				
			||||||
      collection=$(echo "$COLLECTION_LABELS" | tr ';' '\n' | awk '/./ {print}' | $FZF --margin="30%" --no-info --delimiter='=' --with-nth=2 --accept-nth=1)
 | 
					 | 
				
			||||||
      if [ -z "$collection" ]; then
 | 
					 | 
				
			||||||
        exit
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
      __import_to_collection "$file" "$collection"
 | 
					 | 
				
			||||||
      break
 | 
					 | 
				
			||||||
      ;;
 | 
					 | 
				
			||||||
    "no")
 | 
					 | 
				
			||||||
      break
 | 
					 | 
				
			||||||
      ;;
 | 
					 | 
				
			||||||
    *)
 | 
					 | 
				
			||||||
      echo "Please answer \"yes\" or \"no\"." >/dev/tty
 | 
					 | 
				
			||||||
      ;;
 | 
					 | 
				
			||||||
    esac
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
  rm -f "$filetmp"
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Run git command
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $2..: Git command
 | 
					 | 
				
			||||||
# @return: On success, returns 0, otherwise 1
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--git" ]; then
 | 
					 | 
				
			||||||
  if [ -z "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
    err "Git not supported, run \`$0 --git-init\` first"
 | 
					 | 
				
			||||||
    return 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  shift
 | 
					 | 
				
			||||||
  $GIT "$@"
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Enable the ues of git
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @return: On success, returns 0, otherwise 1
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--git-init" ]; then
 | 
					 | 
				
			||||||
  if [ -n "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
    err "Git already enabled"
 | 
					 | 
				
			||||||
    return 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  if ! command -v "git" >/dev/null; then
 | 
					 | 
				
			||||||
    err "Git command not found"
 | 
					 | 
				
			||||||
    return 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  git -C "$ROOT" init
 | 
					 | 
				
			||||||
  git -C "$ROOT" add -A
 | 
					 | 
				
			||||||
  git -C "$ROOT" commit -m 'Initial commit: Start git tracking'
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
@@ -1,100 +0,0 @@
 | 
				
			|||||||
# Preview command-line options
 | 
					 | 
				
			||||||
# - --preview-event
 | 
					 | 
				
			||||||
# - --preview_week
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Print preview of event and exit.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $2: Line from day view containing an event
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--preview-event" ]; then
 | 
					 | 
				
			||||||
  hour=$(echo "$2" | cut -f 2)
 | 
					 | 
				
			||||||
  start=$(echo "$2" | cut -f 3)
 | 
					 | 
				
			||||||
  end=$(echo "$2" | cut -f 4)
 | 
					 | 
				
			||||||
  fpath=$(echo "$2" | cut -f 5)
 | 
					 | 
				
			||||||
  if [ -n "$hour" ] && [ -n "$fpath" ]; then
 | 
					 | 
				
			||||||
    fpath="$ROOT/$fpath"
 | 
					 | 
				
			||||||
    start=$(datetime_str "$start" "%a ")
 | 
					 | 
				
			||||||
    end=$(datetime_str "$end" "%a ")
 | 
					 | 
				
			||||||
    location=$(awk -v field="LOCATION" "$AWK_GET" "$fpath")
 | 
					 | 
				
			||||||
    status=$(awk -v field="STATUS" "$AWK_GET" "$fpath")
 | 
					 | 
				
			||||||
    if [ "$status" = "TENTATIVE" ]; then
 | 
					 | 
				
			||||||
      symb="🟡"
 | 
					 | 
				
			||||||
    elif [ "$status" = "CANCELLED" ]; then
 | 
					 | 
				
			||||||
      symb="❌"
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    echo "📅${symb:-} ${STYLE_EPV_DATETIME}$start${OFF} → ${STYLE_EPV_DATETIME}$end${OFF}"
 | 
					 | 
				
			||||||
    if [ -n "${location:-}" ]; then
 | 
					 | 
				
			||||||
      echo "📍 ${STYLE_EPV_LOCATION}$location${OFF}"
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    attcnt=$(awk "$AWK_ATTACHLS" "$fpath" | wc -l)
 | 
					 | 
				
			||||||
    if [ "$attcnt" -gt 0 ]; then
 | 
					 | 
				
			||||||
      echo "🔗 $attcnt attachments"
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    echo ""
 | 
					 | 
				
			||||||
    awk -v field="DESCRIPTION" "$AWK_GET" "$fpath" | $CAT
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Print preview of week.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $2: Line from week view
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--preview-week" ]; then
 | 
					 | 
				
			||||||
  sign=$(echo "$2" | cut -f 1)
 | 
					 | 
				
			||||||
  if [ "$sign" = "+" ]; then
 | 
					 | 
				
			||||||
    startdate=$(echo "$2" | cut -f 2)
 | 
					 | 
				
			||||||
    set -- $(date -d "$startdate" +"%Y %m %d")
 | 
					 | 
				
			||||||
    year=$1
 | 
					 | 
				
			||||||
    month=$2
 | 
					 | 
				
			||||||
    day=$3
 | 
					 | 
				
			||||||
    set -- $(date -d "today" +"%Y %m %d")
 | 
					 | 
				
			||||||
    year_cur=$1
 | 
					 | 
				
			||||||
    month_cur=$2
 | 
					 | 
				
			||||||
    day_cur=$3
 | 
					 | 
				
			||||||
    # Previous months
 | 
					 | 
				
			||||||
    set -- $(month_previous "$month" "$year")
 | 
					 | 
				
			||||||
    month_pre="$1"
 | 
					 | 
				
			||||||
    year_pre="$2"
 | 
					 | 
				
			||||||
    set -- $(month_previous "$month_pre" "$year_pre")
 | 
					 | 
				
			||||||
    month_pre2="$1"
 | 
					 | 
				
			||||||
    year_pre2="$2"
 | 
					 | 
				
			||||||
    # Next months
 | 
					 | 
				
			||||||
    set -- $(month_next "$month" "$year")
 | 
					 | 
				
			||||||
    month_nex="$1"
 | 
					 | 
				
			||||||
    year_nex="$2"
 | 
					 | 
				
			||||||
    set -- $(month_next "$month_nex" "$year_nex")
 | 
					 | 
				
			||||||
    month_nex2="$1"
 | 
					 | 
				
			||||||
    year_nex2="$2"
 | 
					 | 
				
			||||||
    set -- $(month_next "$month_nex2" "$year_nex2")
 | 
					 | 
				
			||||||
    month_nex3="$1"
 | 
					 | 
				
			||||||
    year_nex3="$2"
 | 
					 | 
				
			||||||
    # Highlight today
 | 
					 | 
				
			||||||
    if [ "$month_pre2" -eq "$month_cur" ] && [ "$year_pre2" -eq "$year_cur" ]; then
 | 
					 | 
				
			||||||
      var_pre2=$day_cur
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    if [ "$month_pre" -eq "$month_cur" ] && [ "$year_pre" -eq "$year_cur" ]; then
 | 
					 | 
				
			||||||
      var_pre=$day_cur
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    if [ "$month" -eq "$month_cur" ] && [ "$year" -eq "$year_cur" ]; then
 | 
					 | 
				
			||||||
      var=$day_cur
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    if [ "$month_nex" -eq "$month_cur" ] && [ "$year_nex" -eq "$year_cur" ]; then
 | 
					 | 
				
			||||||
      var_nex=$day_cur
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    if [ "$month_nex2" -eq "$month_cur" ] && [ "$year_nex2" -eq "$year_cur" ]; then
 | 
					 | 
				
			||||||
      var_nex2=$day_cur
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    if [ "$month_nex3" -eq "$month_cur" ] && [ "$year_nex3" -eq "$year_cur" ]; then
 | 
					 | 
				
			||||||
      var_nex3=$day_cur
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    # show
 | 
					 | 
				
			||||||
    (
 | 
					 | 
				
			||||||
      cal "$month_pre2" "$year_pre2" | awk "$AWK_CALSHIFT" | awk -v cur="${var_pre2:-}" -v style_month="$STYLE_CALENDAR_MONTH" -v style_weekdays="$STYLE_CALENDAR_WEEKDAYS" -v style_cur="$STYLE_CALENDAR_CURRENT_DAY" -v style_highlight="$STYLE_CALENDAR_HL_DAY" -v style_weekdays="$STYLE_CALENDAR_WEEKDAYS" -v style_cur="$STYLE_CALENDAR_CURRENT_DAY" -v style_highlight="$STYLE_CALENDAR_HL_DAY" "$AWK_CALANNOT"
 | 
					 | 
				
			||||||
      cal "$month_pre" "$year_pre" | awk "$AWK_CALSHIFT" | awk -v cur="${var_pre:-}" -v style_month="$STYLE_CALENDAR_MONTH" -v style_weekdays="$STYLE_CALENDAR_WEEKDAYS" -v style_cur="$STYLE_CALENDAR_CURRENT_DAY" -v style_highlight="$STYLE_CALENDAR_HL_DAY" "$AWK_CALANNOT"
 | 
					 | 
				
			||||||
      cal "$month" "$year" | awk "$AWK_CALSHIFT" | awk -v cur="${var:-}" -v day="$day" -v style_month="$STYLE_CALENDAR_MONTH" -v style_weekdays="$STYLE_CALENDAR_WEEKDAYS" -v style_cur="$STYLE_CALENDAR_CURRENT_DAY" -v style_highlight="$STYLE_CALENDAR_HL_DAY" "$AWK_CALANNOT"
 | 
					 | 
				
			||||||
      cal "$month_nex" "$year_nex" | awk "$AWK_CALSHIFT" | awk -v cur="${var_nex:-}" -v style_month="$STYLE_CALENDAR_MONTH" -v style_weekdays="$STYLE_CALENDAR_WEEKDAYS" -v style_cur="$STYLE_CALENDAR_CURRENT_DAY" -v style_highlight="$STYLE_CALENDAR_HL_DAY" "$AWK_CALANNOT"
 | 
					 | 
				
			||||||
      cal "$month_nex2" "$year_nex2" | awk "$AWK_CALSHIFT" | awk -v cur="${var_nex2:-}" -v style_month="$STYLE_CALENDAR_MONTH" -v style_weekdays="$STYLE_CALENDAR_WEEKDAYS" -v style_cur="$STYLE_CALENDAR_CURRENT_DAY" -v style_highlight="$STYLE_CALENDAR_HL_DAY" "$AWK_CALANNOT"
 | 
					 | 
				
			||||||
      cal "$month_nex3" "$year_nex3" | awk "$AWK_CALSHIFT" | awk -v cur="${var_nex3:-}" -v style_month="$STYLE_CALENDAR_MONTH" -v style_weekdays="$STYLE_CALENDAR_WEEKDAYS" -v style_cur="$STYLE_CALENDAR_CURRENT_DAY" -v style_highlight="$STYLE_CALENDAR_HL_DAY" "$AWK_CALANNOT"
 | 
					 | 
				
			||||||
    ) | awk '{ l[(NR-1)%8] = l[(NR-1)%8] "    " $0 } END {for (i in l) print l[i] }'
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
@@ -1,31 +0,0 @@
 | 
				
			|||||||
# Command-line Arguments for reloading views
 | 
					 | 
				
			||||||
# - --reload-day
 | 
					 | 
				
			||||||
# - --reload-week
 | 
					 | 
				
			||||||
# - --reload-all
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Reload view of specified day.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $2.. (optional): Specification of day, defaults to `today`
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--reload-day" ]; then
 | 
					 | 
				
			||||||
  shift
 | 
					 | 
				
			||||||
  DISPLAY_DATE=${*:-today}
 | 
					 | 
				
			||||||
  __view_day
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Reload view of the week containing the specified date.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $2.. (optional): Specification of day, defaults to `today`
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--reload-week" ]; then
 | 
					 | 
				
			||||||
  shift
 | 
					 | 
				
			||||||
  DISPLAY_DATE=${*:-today}
 | 
					 | 
				
			||||||
  DISPLAY_POS=$((8 - $(date -d "$DISPLAY_DATE" +"%u")))
 | 
					 | 
				
			||||||
  __view_week
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Reload view of all entries.
 | 
					 | 
				
			||||||
if [ "${1:-}" = "--reload-all" ]; then
 | 
					 | 
				
			||||||
  __view_all
 | 
					 | 
				
			||||||
  exit
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
@@ -1,66 +0,0 @@
 | 
				
			|||||||
# Load Configuration
 | 
					 | 
				
			||||||
# - ROOT:                 Directory containing the collections
 | 
					 | 
				
			||||||
# - COLLECTION_LABELS:    Mappings between collections and labels
 | 
					 | 
				
			||||||
# - SYNC_CMD (optional):  Synchronization command
 | 
					 | 
				
			||||||
# - DAY_START (optional): Hour of start of the day (defaults to 8)
 | 
					 | 
				
			||||||
# - DAY_END (optional):   Hour of end of the day (defaults to 18)
 | 
					 | 
				
			||||||
# - EDITOR (optional):    Your favorite editor, is usually already exported
 | 
					 | 
				
			||||||
# - TZ (optional):        Your favorite timezone, usually system's choice
 | 
					 | 
				
			||||||
# - LC_TIME (optional):   Your favorite locale for date and time
 | 
					 | 
				
			||||||
# - ZI_DIR (optional):    Location of tzdata, defaults to /usr/share/zoneinfo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CONFIGFILE="$HOME/.config/fzf-vcal/config"
 | 
					 | 
				
			||||||
if [ ! -f "$CONFIGFILE" ]; then
 | 
					 | 
				
			||||||
  err "Configuration '$CONFIGFILE' not found."
 | 
					 | 
				
			||||||
  exit 1
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
# shellcheck source=/dev/null
 | 
					 | 
				
			||||||
. "$CONFIGFILE"
 | 
					 | 
				
			||||||
if [ -z "${ROOT:-}" ] || [ -z "${COLLECTION_LABELS:-}" ]; then
 | 
					 | 
				
			||||||
  err "Configuration is incomplete."
 | 
					 | 
				
			||||||
  exit 1
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
export ROOT COLLECTION_LABELS
 | 
					 | 
				
			||||||
export SYNC_CMD=${SYNC_CMD:-echo 'Synchronization disabled'}
 | 
					 | 
				
			||||||
export DAY_START=${DAY_START:-8}
 | 
					 | 
				
			||||||
export DAY_END=${DAY_END:-18}
 | 
					 | 
				
			||||||
export ZI_DIR=${ZI_DIR:-/usr/share/zoneinfo/posix}
 | 
					 | 
				
			||||||
if [ ! -d "$ZI_DIR" ]; then
 | 
					 | 
				
			||||||
  err "Could not determine time-zone information"
 | 
					 | 
				
			||||||
  exit 1
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
export OPEN=${OPEN:-open}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Check and load required tools
 | 
					 | 
				
			||||||
# - FZF:     Fuzzy finder `fzf``
 | 
					 | 
				
			||||||
# - UUIDGEN: Tool `uuidgen` to generate random uids
 | 
					 | 
				
			||||||
# - CAT:     `bat` or `batcat` or `cat`
 | 
					 | 
				
			||||||
# - GIT:     `git` if it exists
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# The presence of POSIX tools is not checked.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if command -v "fzf" >/dev/null; then
 | 
					 | 
				
			||||||
  export FZF="fzf --black"
 | 
					 | 
				
			||||||
else
 | 
					 | 
				
			||||||
  err "Did not find the command-line fuzzy finder fzf."
 | 
					 | 
				
			||||||
  exit 1
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if command -v "uuidgen" >/dev/null; then
 | 
					 | 
				
			||||||
  export UUIDGEN="uuidgen"
 | 
					 | 
				
			||||||
else
 | 
					 | 
				
			||||||
  err "Did not find the uuidgen command."
 | 
					 | 
				
			||||||
  exit 1
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if command -v "bat" >/dev/null; then
 | 
					 | 
				
			||||||
  CAT="bat"
 | 
					 | 
				
			||||||
elif command -v "batcat" >/dev/null; then
 | 
					 | 
				
			||||||
  CAT="batcat"
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
CAT=${CAT:+$CAT --color=always --style=numbers --language=md}
 | 
					 | 
				
			||||||
export CAT=${CAT:-cat}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if command -v "git" >/dev/null && [ -d "$ROOT/.git" ]; then
 | 
					 | 
				
			||||||
  export GIT="git -C $ROOT"
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
@@ -1,233 +0,0 @@
 | 
				
			|||||||
# iCalendar modification wrapper
 | 
					 | 
				
			||||||
# - __edit
 | 
					 | 
				
			||||||
# - __new
 | 
					 | 
				
			||||||
# - __delete
 | 
					 | 
				
			||||||
# - __import_to_collection
 | 
					 | 
				
			||||||
# - __cancel_toggle
 | 
					 | 
				
			||||||
# - __tentative_toggle
 | 
					 | 
				
			||||||
# - __add_attachment
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Edit iCalendar file.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: Start date/date-time
 | 
					 | 
				
			||||||
# @input $2: End date/date-time
 | 
					 | 
				
			||||||
# @input $3: Path to iCalendar file (relative to `$ROOT`)
 | 
					 | 
				
			||||||
__edit() {
 | 
					 | 
				
			||||||
  start=$(__datetime_human_machine "$1")
 | 
					 | 
				
			||||||
  end=$(__datetime_human_machine "$2")
 | 
					 | 
				
			||||||
  fpath="$ROOT/$3"
 | 
					 | 
				
			||||||
  location=$(awk -v field="LOCATION" "$AWK_GET" "$fpath" | tr -d "\n")
 | 
					 | 
				
			||||||
  summary=$(awk -v field="SUMMARY" "$AWK_GET" "$fpath" | tr -d "\n")
 | 
					 | 
				
			||||||
  description=$(awk -v field="DESCRIPTION" "$AWK_GET" "$fpath")
 | 
					 | 
				
			||||||
  filetmp=$(mktemp --suffix='.md')
 | 
					 | 
				
			||||||
  printf "::: |> %s\n::: <| %s\n" "$start" "$end" >"$filetmp"
 | 
					 | 
				
			||||||
  if [ -n "$location" ]; then
 | 
					 | 
				
			||||||
    printf "@ %s\n" "$location" >>"$filetmp"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  printf "# %s\n\n%s\n" "$summary" "$description" >>"$filetmp"
 | 
					 | 
				
			||||||
  checksum=$(cksum "$filetmp")
 | 
					 | 
				
			||||||
  $EDITOR "$filetmp" >/dev/tty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # Update only if changes are detected
 | 
					 | 
				
			||||||
  if [ "$checksum" != "$(cksum "$filetmp")" ]; then
 | 
					 | 
				
			||||||
    filenew="$filetmp.ics"
 | 
					 | 
				
			||||||
    if awk "$AWK_UPDATE" "$filetmp" "$fpath" >"$filenew"; then
 | 
					 | 
				
			||||||
      mv "$filenew" "$fpath"
 | 
					 | 
				
			||||||
      if [ -n "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
        $GIT add "$fpath"
 | 
					 | 
				
			||||||
        $GIT commit -q -m "Modified event '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
      __refresh_data
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      rm -f "$filenew"
 | 
					 | 
				
			||||||
      err "Failed to edit entry. Press <enter> to continue."
 | 
					 | 
				
			||||||
      read -r tmp
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  rm "$filetmp"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Generate new iCalendar file
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This function also sets the `$start` variable to the start of the new entry.
 | 
					 | 
				
			||||||
# On failure, start will be empty.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# If some start has been specified and the nanoseconds are not 0, we assume
 | 
					 | 
				
			||||||
# that the user entered "tomorrow" or something like that, and did not
 | 
					 | 
				
			||||||
# specify the time. So, we will use the `$DAY_START` time of that date.
 | 
					 | 
				
			||||||
# If the user specified a malformed date/date-time, we fail.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1 (optional): Date or datetime, defaults to today.
 | 
					 | 
				
			||||||
__new() {
 | 
					 | 
				
			||||||
  collection=$(echo "$COLLECTION_LABELS" | tr ';' '\n' | awk '/./ {print}' | $FZF --margin="30%" --no-info --delimiter='=' --with-nth=2 --accept-nth=1)
 | 
					 | 
				
			||||||
  fpath=""
 | 
					 | 
				
			||||||
  while [ -f "$fpath" ] || [ -z "$fpath" ]; do
 | 
					 | 
				
			||||||
    uuid=$($UUIDGEN)
 | 
					 | 
				
			||||||
    fpath="$ROOT/$collection/$uuid.ics"
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
  d="today $DAY_START"
 | 
					 | 
				
			||||||
  if [ -n "${1:-}" ]; then
 | 
					 | 
				
			||||||
    d="$1"
 | 
					 | 
				
			||||||
    if [ "$(date -d "$1" +"%N")" -ne 0 ]; then
 | 
					 | 
				
			||||||
      d="$d $DAY_START:00"
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  startsec=$(date -d "$d" +"%s")
 | 
					 | 
				
			||||||
  endsec=$((startsec + 3600))
 | 
					 | 
				
			||||||
  start=$(__datetime_human_machine "$startsec")
 | 
					 | 
				
			||||||
  end=$(__datetime_human_machine "$endsec")
 | 
					 | 
				
			||||||
  filetmp=$(mktemp --suffix='.md')
 | 
					 | 
				
			||||||
  (
 | 
					 | 
				
			||||||
    echo "::: |> $start"
 | 
					 | 
				
			||||||
    echo "::: <| $end"
 | 
					 | 
				
			||||||
    echo "@ <!-- write location here, optional line -->"
 | 
					 | 
				
			||||||
    echo "# <!-- write summary here -->"
 | 
					 | 
				
			||||||
    echo ""
 | 
					 | 
				
			||||||
  ) >"$filetmp"
 | 
					 | 
				
			||||||
  checksum=$(cksum "$filetmp")
 | 
					 | 
				
			||||||
  $EDITOR "$filetmp" >/dev/tty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # Update only if changes are detected
 | 
					 | 
				
			||||||
  if [ "$checksum" != "$(cksum "$filetmp")" ]; then
 | 
					 | 
				
			||||||
    filenew="$filetmp.ics"
 | 
					 | 
				
			||||||
    if awk -v uid="$uuid" "$AWK_NEW" "$filetmp" >"$filenew"; then
 | 
					 | 
				
			||||||
      mv "$filenew" "$fpath"
 | 
					 | 
				
			||||||
      if [ -n "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
        $GIT add "$fpath"
 | 
					 | 
				
			||||||
        $GIT commit -q -m "Added event '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
      start=$(awk -v field="DTSTART" "$AWK_GET" "$fpath" | grep -o '[0-9]\{8\}')
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      rm -f "$filenew"
 | 
					 | 
				
			||||||
      start=""
 | 
					 | 
				
			||||||
      err "Failed to create new entry. Press <enter> to continue."
 | 
					 | 
				
			||||||
      read -r tmp
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  rm "$filetmp"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Delete iCalendar file
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: Path to iCalendar file, relative to `$ROOT`
 | 
					 | 
				
			||||||
__delete() {
 | 
					 | 
				
			||||||
  fpath="$ROOT/$1"
 | 
					 | 
				
			||||||
  summary=$(awk -v field="SUMMARY" "$AWK_GET" "$fpath")
 | 
					 | 
				
			||||||
  while true; do
 | 
					 | 
				
			||||||
    printf "Do you want to delete the entry with the title \"%s\"? (yes/no): " "$summary" >/dev/tty
 | 
					 | 
				
			||||||
    read -r yn
 | 
					 | 
				
			||||||
    case $yn in
 | 
					 | 
				
			||||||
    "yes")
 | 
					 | 
				
			||||||
      sfg="$(__summary_for_commit "$fpath")"
 | 
					 | 
				
			||||||
      rm -v "$fpath"
 | 
					 | 
				
			||||||
      if [ -n "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
        $GIT add "$fpath"
 | 
					 | 
				
			||||||
        $GIT commit -q -m "Deleted event '$sfg ...'" -- "$fpath"
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
      break
 | 
					 | 
				
			||||||
      ;;
 | 
					 | 
				
			||||||
    "no")
 | 
					 | 
				
			||||||
      break
 | 
					 | 
				
			||||||
      ;;
 | 
					 | 
				
			||||||
    *)
 | 
					 | 
				
			||||||
      echo "Please answer \"yes\" or \"no\"." >/dev/tty
 | 
					 | 
				
			||||||
      ;;
 | 
					 | 
				
			||||||
    esac
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Import iCalendar file to specified collection. The only modification made to
 | 
					 | 
				
			||||||
# the file is setting the UID.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: path to iCalendar file
 | 
					 | 
				
			||||||
# @input $2: collection name
 | 
					 | 
				
			||||||
__import_to_collection() {
 | 
					 | 
				
			||||||
  file="$1"
 | 
					 | 
				
			||||||
  collection="$2"
 | 
					 | 
				
			||||||
  fpath=""
 | 
					 | 
				
			||||||
  while [ -f "$fpath" ] || [ -z "$fpath" ]; do
 | 
					 | 
				
			||||||
    uuid=$($UUIDGEN)
 | 
					 | 
				
			||||||
    fpath="$ROOT/$collection/$uuid.ics"
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
  filetmp=$(mktemp)
 | 
					 | 
				
			||||||
  awk -v field="UID" -v value="$uuid" "$AWK_SET" "$file" >"$filetmp"
 | 
					 | 
				
			||||||
  mv "$filetmp" "$fpath"
 | 
					 | 
				
			||||||
  if [ -n "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
    $GIT add "$fpath"
 | 
					 | 
				
			||||||
    $GIT commit -q -m "Imported event '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Set status of appointment to CANCELLED or CONFIRMED (toggle)
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: path to iCalendar file
 | 
					 | 
				
			||||||
__cancel_toggle() {
 | 
					 | 
				
			||||||
  fpath="$ROOT/$1"
 | 
					 | 
				
			||||||
  status=$(awk -v field="STATUS" "$AWK_GET" "$fpath")
 | 
					 | 
				
			||||||
  newstatus="CANCELLED"
 | 
					 | 
				
			||||||
  if [ "${status:-}" = "$newstatus" ]; then
 | 
					 | 
				
			||||||
    newstatus="CONFIRMED"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  filetmp=$(mktemp)
 | 
					 | 
				
			||||||
  awk -v field="STATUS" -v value="$newstatus" "$AWK_SET" "$fpath" >"$filetmp"
 | 
					 | 
				
			||||||
  mv "$filetmp" "$fpath"
 | 
					 | 
				
			||||||
  if [ -n "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
    $GIT add "$fpath"
 | 
					 | 
				
			||||||
    $GIT commit -q -m "Event '$(__summary_for_commit "$fpath") ...' has now status $status" -- "$fpath"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Toggle status flag: CONFIRMED <-> TENTATIVE
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: path to iCalendar file
 | 
					 | 
				
			||||||
__tentative_toggle() {
 | 
					 | 
				
			||||||
  fpath="$ROOT/$1"
 | 
					 | 
				
			||||||
  status=$(awk -v field="STATUS" "$AWK_GET" "$fpath")
 | 
					 | 
				
			||||||
  newstatus="TENTATIVE"
 | 
					 | 
				
			||||||
  if [ "${status:-}" = "$newstatus" ]; then
 | 
					 | 
				
			||||||
    newstatus="CONFIRMED"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  filetmp=$(mktemp)
 | 
					 | 
				
			||||||
  awk -v field="STATUS" -v value="$newstatus" "$AWK_SET" "$fpath" >"$filetmp"
 | 
					 | 
				
			||||||
  mv "$filetmp" "$fpath"
 | 
					 | 
				
			||||||
  if [ -n "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
    $GIT add "$fpath"
 | 
					 | 
				
			||||||
    $GIT commit -q -m "Event '$(__summary_for_commit "$fpath") ...' has now status $status" -- "$fpath"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Prepend attachment to iCalendar file
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: path to iCalendar file
 | 
					 | 
				
			||||||
__add_attachment() {
 | 
					 | 
				
			||||||
  fpath="$ROOT/$1"
 | 
					 | 
				
			||||||
  sel=$(
 | 
					 | 
				
			||||||
    $FZF --prompt="Select attachment> " \
 | 
					 | 
				
			||||||
      --walker="file,hidden" \
 | 
					 | 
				
			||||||
      --walker-root="$HOME" \
 | 
					 | 
				
			||||||
      --expect="ctrl-c,ctrl-g,ctrl-q,esc"
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  key=$(echo "$sel" | head -1)
 | 
					 | 
				
			||||||
  f=$(echo "$sel" | tail -1)
 | 
					 | 
				
			||||||
  if [ -n "$key" ]; then
 | 
					 | 
				
			||||||
    f=""
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  if [ -z "$f" ] || [ ! -f "$f" ]; then
 | 
					 | 
				
			||||||
    return
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  filename=$(basename "$f")
 | 
					 | 
				
			||||||
  mime=$(file -b -i "$f" | cut -d ';' -f 1)
 | 
					 | 
				
			||||||
  if [ -z "$mime" ]; then
 | 
					 | 
				
			||||||
    mime="application/octet-stream"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  fenc=$(mktemp)
 | 
					 | 
				
			||||||
  base64 "$f" >"$fenc"
 | 
					 | 
				
			||||||
  filetmp=$(mktemp)
 | 
					 | 
				
			||||||
  awk -v file="$fenc" -v mime="$mime" -v filename="$filename" "$AWK_ATTACH" "$fpath" >"$filetmp"
 | 
					 | 
				
			||||||
  mv "$filetmp" "$fpath"
 | 
					 | 
				
			||||||
  if [ -n "${GIT:-}" ]; then
 | 
					 | 
				
			||||||
    $GIT add "$fpath"
 | 
					 | 
				
			||||||
    $GIT commit -q -m "Added attachment to '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  rm "$fenc"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,45 +0,0 @@
 | 
				
			|||||||
# Loading functions
 | 
					 | 
				
			||||||
# - __load_approx_data
 | 
					 | 
				
			||||||
# - __load_weeks
 | 
					 | 
				
			||||||
# - __refresh_data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Print approximate data from iCalendar files in `$ROOT`
 | 
					 | 
				
			||||||
__load_approx_data() {
 | 
					 | 
				
			||||||
  find "$ROOT" -type f -name '*.ics' -print0 |
 | 
					 | 
				
			||||||
    xargs -0 -P0 \
 | 
					 | 
				
			||||||
      awk \
 | 
					 | 
				
			||||||
      -v collection_labels="$COLLECTION_LABELS" \
 | 
					 | 
				
			||||||
      -v style_line="$STYLE_LV" \
 | 
					 | 
				
			||||||
      "$AWK_APPROX"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# For every relevant week, print associated iCalendar files
 | 
					 | 
				
			||||||
__load_weeks() {
 | 
					 | 
				
			||||||
  dates=$(awk -F'\t' '{ print $2; print $3 }' "$APPROX_DATA_FILE")
 | 
					 | 
				
			||||||
  file_dates=$(mktemp)
 | 
					 | 
				
			||||||
  echo "$dates" | date --file="/dev/stdin" +"%G:%V:" >"$file_dates"
 | 
					 | 
				
			||||||
  awk "$AWK_MERGE" "$file_dates" "$APPROX_DATA_FILE"
 | 
					 | 
				
			||||||
  rm "$file_dates"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Refresh approximate data and per-week data.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This functions stores the output of `__load_approx_data` in the temporary
 | 
					 | 
				
			||||||
# file `$APPROX_DATA_FILE` and the output of `__load_weeks` in the temporary
 | 
					 | 
				
			||||||
# file `@WEEKLY_DATA_FILE`.
 | 
					 | 
				
			||||||
__refresh_data() {
 | 
					 | 
				
			||||||
  if [ -z "${APPROX_DATA_FILE:-}" ]; then
 | 
					 | 
				
			||||||
    APPROX_DATA_FILE=$(mktemp)
 | 
					 | 
				
			||||||
    trap 'rm -f "$APPROX_DATA_FILE"' EXIT INT
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  if [ -z "${WEEKLY_DATA_FILE:-}" ]; then
 | 
					 | 
				
			||||||
    WEEKLY_DATA_FILE=$(mktemp)
 | 
					 | 
				
			||||||
    trap 'rm -f "$WEEKLY_DATA_FILE"' EXIT INT
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  debug "__refresh_data(): going to load approx data"
 | 
					 | 
				
			||||||
  __load_approx_data >"$APPROX_DATA_FILE"
 | 
					 | 
				
			||||||
  debug "__refresh_data(): approx data loaded"
 | 
					 | 
				
			||||||
  debug "__refresh_data(): going to load weeks"
 | 
					 | 
				
			||||||
  __load_weeks >"$WEEKLY_DATA_FILE"
 | 
					 | 
				
			||||||
  debug "__refresh_data(): weeks loaded"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,44 +0,0 @@
 | 
				
			|||||||
# err()
 | 
					 | 
				
			||||||
# This is a helper function to print errors.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: Error message
 | 
					 | 
				
			||||||
err() {
 | 
					 | 
				
			||||||
  echo "❌ $1" >/dev/tty
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# debug()
 | 
					 | 
				
			||||||
# Pring debug message to fzf-vcal.debug
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: Debug message
 | 
					 | 
				
			||||||
debug() {
 | 
					 | 
				
			||||||
  echo "$(date +"%D %T.%N"): $1" >>"/tmp/fzf-vcal.debug"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Print date or datetime in a human and machine readable form.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: Seconds since epoch
 | 
					 | 
				
			||||||
__datetime_human_machine() {
 | 
					 | 
				
			||||||
  s="$1"
 | 
					 | 
				
			||||||
  t=$(date -d "@$s" +"%R")
 | 
					 | 
				
			||||||
  dfmt="%F"
 | 
					 | 
				
			||||||
  if [ "$t" != "00:00" ]; then
 | 
					 | 
				
			||||||
    dfmt="$dfmt %R"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  date -d "@$s" +"$dfmt"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Get summary string that can be used in for git-commit messages.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: iCalendar file path
 | 
					 | 
				
			||||||
__summary_for_commit() {
 | 
					 | 
				
			||||||
  awk -v field="SUMMARY" "$AWK_GET" "$1" | tr -c -d "[:alnum:][:blank:]" | head -c 15
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Re-export dynamical variables to subshells.
 | 
					 | 
				
			||||||
__export() {
 | 
					 | 
				
			||||||
  DISPLAY_DATE=$(date -R -d "$DISPLAY_DATE")
 | 
					 | 
				
			||||||
  export DISPLAY_DATE
 | 
					 | 
				
			||||||
  if [ -n "${TZ:-}" ]; then
 | 
					 | 
				
			||||||
    export TZ
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,51 +0,0 @@
 | 
				
			|||||||
# Preview helper functions
 | 
					 | 
				
			||||||
# - month_previous
 | 
					 | 
				
			||||||
# - month_next
 | 
					 | 
				
			||||||
# - datetime_str
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Print previous month of specified input month as <month> <year>.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: Month
 | 
					 | 
				
			||||||
# @input $2: Year
 | 
					 | 
				
			||||||
month_previous() {
 | 
					 | 
				
			||||||
  month=$(echo "$1" | sed 's/^0//')
 | 
					 | 
				
			||||||
  year=$(echo "$2" | sed 's/^0//')
 | 
					 | 
				
			||||||
  if [ "$month" -eq 1 ]; then
 | 
					 | 
				
			||||||
    month=12
 | 
					 | 
				
			||||||
    year=$((year - 1))
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    month=$((month - 1))
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  echo "$month $year"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Print next month of specified input month as <month> <year>.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1: Month
 | 
					 | 
				
			||||||
# @input $2: Year
 | 
					 | 
				
			||||||
month_next() {
 | 
					 | 
				
			||||||
  month=$(echo "$1" | sed 's/^0//')
 | 
					 | 
				
			||||||
  year=$(echo "$2" | sed 's/^0//')
 | 
					 | 
				
			||||||
  if [ "$month" -eq 12 ]; then
 | 
					 | 
				
			||||||
    month=1
 | 
					 | 
				
			||||||
    year=$((year + 1))
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    month=$((month + 1))
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  echo "$month $year"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Print date or datetime in a human readable form.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# @input $1:              Seconds since epoch
 | 
					 | 
				
			||||||
# @input $2.. (optoinal): Prepend date format
 | 
					 | 
				
			||||||
datetime_str() {
 | 
					 | 
				
			||||||
  s="$1"
 | 
					 | 
				
			||||||
  shift
 | 
					 | 
				
			||||||
  t=$(date -d "@$s" +"%R")
 | 
					 | 
				
			||||||
  dfmt="$*%e %b %Y"
 | 
					 | 
				
			||||||
  if [ "$t" != "00:00" ]; then
 | 
					 | 
				
			||||||
    dfmt="$dfmt %R %Z"
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  date -d "@$s" +"$dfmt"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,47 +0,0 @@
 | 
				
			|||||||
# Colors
 | 
					 | 
				
			||||||
GREEN="\033[1;32m"
 | 
					 | 
				
			||||||
BLACK="\033[1;30m"
 | 
					 | 
				
			||||||
RED="\033[1;31m"
 | 
					 | 
				
			||||||
WHITE="\033[1;97m"
 | 
					 | 
				
			||||||
CYAN="\033[1;36m"
 | 
					 | 
				
			||||||
LIGHT_CYAN="\033[1;36m"
 | 
					 | 
				
			||||||
STRIKE="\033[9m"
 | 
					 | 
				
			||||||
ITALIC="\033[3m"
 | 
					 | 
				
			||||||
FAINT="\033[2m"
 | 
					 | 
				
			||||||
BOLD="\033[1m"
 | 
					 | 
				
			||||||
BG="\033[41m"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export OFF="\033[m"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Style
 | 
					 | 
				
			||||||
# Calendar
 | 
					 | 
				
			||||||
export STYLE_CALENDAR_MONTH="${STYLE_CALENDAR_MONTH:-$GREEN}"
 | 
					 | 
				
			||||||
export STYLE_CALENDAR_WEEKDAYS="${STYLE_CALENDAR_WEEKDAYS:-$FAINT}"
 | 
					 | 
				
			||||||
export STYLE_CALENDAR_CURRENT_DAY="${STYLE_CALENDAR_CURRENT_DAY:-$BLACK$BG}"
 | 
					 | 
				
			||||||
export STYLE_CALENDAR_HL_DAY="${STYLE_CALENDAR_HL_DAY:-$BOLD$RED}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Week view
 | 
					 | 
				
			||||||
export STYLE_WV_DAY="${STYLE_WV_DAY:-$GREEN}"
 | 
					 | 
				
			||||||
export STYLE_WV_EVENT_DELIM="${STYLE_WV_EVENT_DELIM:-$RED / $OFF}"
 | 
					 | 
				
			||||||
export STYLE_WV_SUMMARY="${STYLE_WV_SUMMARY:-$CYAN}"
 | 
					 | 
				
			||||||
export STYLE_WV_TIME="${STYLE_WV_TIME:-$WHITE}"
 | 
					 | 
				
			||||||
export STYLE_WV_CONFIRMED="${STYLE_WV_CONFIRMED:-$CYAN}"
 | 
					 | 
				
			||||||
export STYLE_WV_TENTATIVE="${STYLE_WV_TENTATIVE:-$FAINT$CYAN}"
 | 
					 | 
				
			||||||
export STYLE_WV_CANCELLED="${STYLE_WV_CANCELLED:-$STRIKE$FAINT$CYAN}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List view
 | 
					 | 
				
			||||||
export STYLE_LV="${STYLE_LV:-$FAINT}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Day view
 | 
					 | 
				
			||||||
export STYLE_DV_ALLDAY="${STYLE_DV_ALLDAY:-$LIGHT_CYAN$ITALIC$FAINT    (allday)   $OFF}"
 | 
					 | 
				
			||||||
export STYLE_DV_TIME="${STYLE_DV_TIME:-$LIGHT_CYAN}"
 | 
					 | 
				
			||||||
export STYLE_DV_CONFIRMED="${STYLE_DV_CONFIRMED:-$CYAN}"
 | 
					 | 
				
			||||||
export STYLE_DV_TENTATIVE="${STYLE_DV_TENTATIVE:-$FAINT$CYAN}"
 | 
					 | 
				
			||||||
export STYLE_DV_CANCELLED="${STYLE_DV_CANCELLED:-$STRIKE$FAINT$CYAN}"
 | 
					 | 
				
			||||||
export STYLE_DV_HOUR="${STYLE_DV_HOUR:-$FAINT}"
 | 
					 | 
				
			||||||
export STYLE_DV_EMPTYHOUR="${STYLE_DV_EMPTYHOUR:-$FAINT----------------------$OFF}"
 | 
					 | 
				
			||||||
export STYLE_DV_TZ="$WHITE$ITALIC"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Event preview
 | 
					 | 
				
			||||||
export STYLE_EPV_DATETIME="${STYLE_EPV_DATETIME:-$CYAN}"
 | 
					 | 
				
			||||||
export STYLE_EPV_LOCATION="${STYLE_EPV_LOCATION:-$GREEN}"
 | 
					 | 
				
			||||||
							
								
								
									
										157
									
								
								src/sh/view.sh
									
									
									
									
									
								
							
							
						
						
									
										157
									
								
								src/sh/view.sh
									
									
									
									
									
								
							@@ -1,157 +0,0 @@
 | 
				
			|||||||
# View Functions
 | 
					 | 
				
			||||||
# - __view_day
 | 
					 | 
				
			||||||
# - __view_week
 | 
					 | 
				
			||||||
# - __view_all
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This function prints the view for the day specified in `$DISPLAY_DATE`, in
 | 
					 | 
				
			||||||
# the tab-delimited format with the fields:
 | 
					 | 
				
			||||||
#  1. start date
 | 
					 | 
				
			||||||
#  2. start time
 | 
					 | 
				
			||||||
#  3. end time
 | 
					 | 
				
			||||||
#  4. file path
 | 
					 | 
				
			||||||
#  5. collection
 | 
					 | 
				
			||||||
#  6. description
 | 
					 | 
				
			||||||
__view_day() {
 | 
					 | 
				
			||||||
  weeknr=$(date -d "$DISPLAY_DATE" +"%G:%V:")
 | 
					 | 
				
			||||||
  files=$(grep "^$weeknr" "$WEEKLY_DATA_FILE" | cut -f 2)
 | 
					 | 
				
			||||||
  # Find relevant files in list of week files
 | 
					 | 
				
			||||||
  sef=$({
 | 
					 | 
				
			||||||
    set -- $files
 | 
					 | 
				
			||||||
    for file in "$@"; do
 | 
					 | 
				
			||||||
      file="$ROOT/$file"
 | 
					 | 
				
			||||||
      awk \
 | 
					 | 
				
			||||||
        -v collection_labels="$COLLECTION_LABELS" \
 | 
					 | 
				
			||||||
        "$AWK_PARSE" "$file"
 | 
					 | 
				
			||||||
    done
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
  # $sef holds (space-delimited): <start> <end> <fpath> <collection> <status> <summary>
 | 
					 | 
				
			||||||
  today=$(date -d "$DISPLAY_DATE" +"%D")
 | 
					 | 
				
			||||||
  if [ -n "$sef" ]; then
 | 
					 | 
				
			||||||
    sef=$(echo "$sef" | while IFS= read -r line; do
 | 
					 | 
				
			||||||
      set -- $line
 | 
					 | 
				
			||||||
      starttime="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      endtime="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      fpath="$1" # we will use | as delimiter (need to convert back!)
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      collection="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      status="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      description="$*"
 | 
					 | 
				
			||||||
      #
 | 
					 | 
				
			||||||
      daystart=$(date -d "$today 00:00:00" +"%s")
 | 
					 | 
				
			||||||
      dayend=$(date -d "$today 23:59:59" +"%s")
 | 
					 | 
				
			||||||
      line=""
 | 
					 | 
				
			||||||
      if [ "$starttime" -gt "$daystart" ] && [ "$starttime" -lt "$dayend" ]; then
 | 
					 | 
				
			||||||
        s=$(date -d "@$starttime" +"%R")
 | 
					 | 
				
			||||||
      elif [ "$starttime" -le "$daystart" ] && [ "$endtime" -gt "$daystart" ]; then
 | 
					 | 
				
			||||||
        s="00:00"
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        continue
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
      if [ "$endtime" -gt "$daystart" ] && [ "$endtime" -lt "$dayend" ]; then
 | 
					 | 
				
			||||||
        e=$(date -d "@$endtime" +"%R")
 | 
					 | 
				
			||||||
      elif [ "$endtime" -ge "$dayend" ] && [ "$starttime" -lt "$dayend" ]; then
 | 
					 | 
				
			||||||
        e="00:00"
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        continue
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
      printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$s" "$e" "$starttime" "$endtime" "$fpath" "$collection" "$description" "$status"
 | 
					 | 
				
			||||||
    done)
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  echo "$sef" | sort -n | awk \
 | 
					 | 
				
			||||||
    -v today="$today" \
 | 
					 | 
				
			||||||
    -v daystart="$DAY_START" \
 | 
					 | 
				
			||||||
    -v dayend="$DAY_END" \
 | 
					 | 
				
			||||||
    -v style_allday="$STYLE_DV_ALLDAY" \
 | 
					 | 
				
			||||||
    -v style_timerange="$STYLE_DV_TIME" \
 | 
					 | 
				
			||||||
    -v style_confirmed="$STYLE_DV_CONFIRMED" \
 | 
					 | 
				
			||||||
    -v style_tentative="$STYLE_DV_TENTATIVE" \
 | 
					 | 
				
			||||||
    -v style_cancelled="$STYLE_DV_CANCELLED" \
 | 
					 | 
				
			||||||
    -v style_hour="$STYLE_DV_HOUR" \
 | 
					 | 
				
			||||||
    -v style_emptyhour="$STYLE_DV_EMPTYHOUR" \
 | 
					 | 
				
			||||||
    "$AWK_DAYVIEW"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This function prints the view for the week that contains the day specified in `$DISPLAY_DATE`.
 | 
					 | 
				
			||||||
__view_week() {
 | 
					 | 
				
			||||||
  debug "__view_week(): Enter"
 | 
					 | 
				
			||||||
  weeknr=$(date -d "$DISPLAY_DATE" +"%G:%V:")
 | 
					 | 
				
			||||||
  files=$(grep "^$weeknr" "$WEEKLY_DATA_FILE" | cut -f 2)
 | 
					 | 
				
			||||||
  dayofweek=$(date -d "$DISPLAY_DATE" +"%u")
 | 
					 | 
				
			||||||
  delta=$((1 - dayofweek))
 | 
					 | 
				
			||||||
  startofweek=$(date -d "$DISPLAY_DATE -$delta days" +"%D")
 | 
					 | 
				
			||||||
  # loop over files
 | 
					 | 
				
			||||||
  debug "__view_week(): loop over files"
 | 
					 | 
				
			||||||
  sef=$({
 | 
					 | 
				
			||||||
    printf "%s" "$files" | xargs -d " " -I {} -P0 \
 | 
					 | 
				
			||||||
      awk \
 | 
					 | 
				
			||||||
      -v collection_labels="$COLLECTION_LABELS" \
 | 
					 | 
				
			||||||
      "$AWK_PARSE" "$ROOT/{}"
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
  debug "__view_week(): loop over files ended"
 | 
					 | 
				
			||||||
  debug "__view_week(): prepare week view"
 | 
					 | 
				
			||||||
  if [ -n "$sef" ]; then
 | 
					 | 
				
			||||||
    sef=$(echo "$sef" | while IFS= read -r line; do
 | 
					 | 
				
			||||||
      set -- $line
 | 
					 | 
				
			||||||
      starttime="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      endtime="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      #fpath="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      collection="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      status="$1"
 | 
					 | 
				
			||||||
      shift
 | 
					 | 
				
			||||||
      if [ "$status" = "TENTATIVE" ]; then
 | 
					 | 
				
			||||||
        symb="$STYLE_WV_TENTATIVE"
 | 
					 | 
				
			||||||
      elif [ "$status" = "CANCELLED" ]; then
 | 
					 | 
				
			||||||
        symb="$STYLE_WV_CANCELLED"
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        symb="$STYLE_WV_CONFIRMED"
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
      description="${symb:-}$*$OFF"
 | 
					 | 
				
			||||||
      for i in $(seq 0 7); do
 | 
					 | 
				
			||||||
        daystart=$(date -d "$startofweek +$i days 00:00:00" +"%s")
 | 
					 | 
				
			||||||
        dayend=$(date -d "$startofweek +$i days 23:59:59" +"%s")
 | 
					 | 
				
			||||||
        if [ "$starttime" -gt "$daystart" ] && [ "$starttime" -lt "$dayend" ]; then
 | 
					 | 
				
			||||||
          s=$(date -d "@$starttime" +"%H:%M")
 | 
					 | 
				
			||||||
        elif [ "$starttime" -le "$daystart" ] && [ "$endtime" -gt "$daystart" ]; then
 | 
					 | 
				
			||||||
          s="00:00"
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
          continue
 | 
					 | 
				
			||||||
        fi
 | 
					 | 
				
			||||||
        if [ "$endtime" -gt "$daystart" ] && [ "$endtime" -lt "$dayend" ]; then
 | 
					 | 
				
			||||||
          e=$(date -d "@$endtime" +"%H:%M")
 | 
					 | 
				
			||||||
        elif [ "$endtime" -ge "$dayend" ] && [ "$starttime" -lt "$dayend" ]; then
 | 
					 | 
				
			||||||
          e="00:00"
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
          continue
 | 
					 | 
				
			||||||
        fi
 | 
					 | 
				
			||||||
        printf "%s\t%s\t%s\t%s\n" "$i" "$s" "$e" "$description"
 | 
					 | 
				
			||||||
      done
 | 
					 | 
				
			||||||
    done)
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  debug "__view_week(): prepare week view ended"
 | 
					 | 
				
			||||||
  debug "__view_week(): generate week view"
 | 
					 | 
				
			||||||
  sef=$({
 | 
					 | 
				
			||||||
    echo "$sef"
 | 
					 | 
				
			||||||
    seq 0 7
 | 
					 | 
				
			||||||
  } | sort -n)
 | 
					 | 
				
			||||||
  echo "$sef" | awk \
 | 
					 | 
				
			||||||
    -v startofweek="$startofweek" \
 | 
					 | 
				
			||||||
    -v style_day="$STYLE_WV_DAY" \
 | 
					 | 
				
			||||||
    -v style_event_delim="$STYLE_WV_EVENT_DELIM" \
 | 
					 | 
				
			||||||
    -v style_summary="$STYLE_WV_SUMMARY" \
 | 
					 | 
				
			||||||
    -v style_time="$STYLE_WV_TIME" \
 | 
					 | 
				
			||||||
    "$AWK_WEEKVIEW"
 | 
					 | 
				
			||||||
  debug "__view_week(): generate week view ended"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This function prints all entries.
 | 
					 | 
				
			||||||
__view_all() {
 | 
					 | 
				
			||||||
  cat "$APPROX_DATA_FILE"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user