Compare commits
	
		
			25 Commits
		
	
	
		
			local
			...
			caec86c5a0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| caec86c5a0 | |||
| 83beaa3ad5 | |||
| ee02a7647b | |||
| bcbd2a9077 | |||
| 0b8066923b | |||
| 06020740cc | |||
| 903c870dba | |||
| 4a17512819 | |||
| 16193b5554 | |||
| e09c38ee29 | |||
| 7ed2df2399 | |||
| 23cbe26d94 | |||
| 13aebae71f | |||
| 93317350f1 | |||
| e67fcca02c | |||
| 735665bb92 | |||
| 0ffa57373a | |||
| 6d8520a016 | |||
| ff898c84c8 | |||
| dc88d5a965 | |||
| cdc008e361 | |||
| 7549acb20c | |||
| aee1a1bf24 | |||
| 4ebcbe36e3 | |||
| acc231027b | 
							
								
								
									
										96
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								README.md
									
									
									
									
									
								
							@@ -1,4 +1,7 @@
 | 
				
			|||||||
A [fzf](https://github.com/junegunn/fzf)-based **calendar** application with CalDav support.
 | 
					A [fzf](https://github.com/junegunn/fzf)-based **calendar** application with CalDav support.
 | 
				
			||||||
 | 
					If you are interested in this, then you may also be interested in the
 | 
				
			||||||
 | 
					corresponding journaling application
 | 
				
			||||||
 | 
					[fzf-vjour](https://github.com/baumea/fzf-vjour).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Description and Use Case
 | 
					Description and Use Case
 | 
				
			||||||
------------------------
 | 
					------------------------
 | 
				
			||||||
@@ -60,37 +63,78 @@ 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 available keybindings:
 | 
					Here is the list of all available keybindings:
 | 
				
			||||||
| Key | View | Action |
 | 
					
 | 
				
			||||||
| --- | ---- | ------ |
 | 
					### Week view
 | 
				
			||||||
| `enter` | week view | Switch to day view |
 | 
					
 | 
				
			||||||
| `ctrl-n` | week view | Make a new entry |
 | 
					| Key | Action |
 | 
				
			||||||
| any letter | week view | Search in the list of all entries |
 | 
					| --- | ------ |
 | 
				
			||||||
| `backspace` on empty query | week view | Undo search |
 | 
					| `q` | quit |
 | 
				
			||||||
| `ctrl-u` | week view | Go back one week |
 | 
					| `enter` | open day |
 | 
				
			||||||
| `ctrl-d` | week view | Go forth one week |
 | 
					| `j` | down |
 | 
				
			||||||
| `ctrl-alt-u` | week view | Go back one month |
 | 
					| `k` | up |
 | 
				
			||||||
| `ctrl-alt-d` | week view | Go forth one month |
 | 
					| `l` | go to next week |
 | 
				
			||||||
| `ctrl-s` | week view | Run the synchronization command |
 | 
					| `h` | go to previous week |
 | 
				
			||||||
| `ctrl-l` | week view | Go to current week |
 | 
					| `ctrl-l` | go to next month |
 | 
				
			||||||
| `ctrl-g` | week view | Goto date |
 | 
					| `ctrl-h` | go to previous month |
 | 
				
			||||||
| `enter` | day view | Open selected  calendar entry in your favorite `$EDITOR` |
 | 
					| `alt-l` | go to next year |
 | 
				
			||||||
| `ctrl-n` | day view | Make a new entry |
 | 
					| `alt-h` | go to previous year |
 | 
				
			||||||
| `esc`, `backspace` or `q` | day view | Go back to week view |
 | 
					| `ctrl-r` | reload and go to week that contains `today` |
 | 
				
			||||||
| `ctrl-s` | day view | Run the synchronization command |
 | 
					| `ctrl-g` | interactively go to specified week |
 | 
				
			||||||
| `ctrl-alt-d` | day view | Delete selected entry |
 | 
					| `ctrl-t` | set timezon |
 | 
				
			||||||
| `j` | day view | Scroll down in preview window |
 | 
					| `ctrl-s` | synchronize |
 | 
				
			||||||
| `k` | day view | Scroll up in preview window |
 | 
					| `ctrl-n` | add new entry |
 | 
				
			||||||
| `w` | day view | Toggle line wrap in preview window ||
 | 
					| `\` | search all appointment s|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Day view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Key | Action |
 | 
				
			||||||
 | 
					| --- | ------ |
 | 
				
			||||||
 | 
					| `enter` | edit 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. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -98,6 +142,10 @@ 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).
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										98
									
								
								src/awk/approx.awk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/awk/approx.awk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					## src/awk/approx.awk
 | 
				
			||||||
 | 
					## Generate single-line approximate information for every iCalendar argument.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## @assign collection_labels: See configuration of the current program.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Time-zone aware parsing of the date/date-time entry at the current record.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @local variables: dt
 | 
				
			||||||
 | 
					# @return: date or date-time string that can be used in date (1)
 | 
				
			||||||
 | 
					function parse(    dt) {
 | 
				
			||||||
 | 
					  # Get timezone information
 | 
				
			||||||
 | 
					  for (i=2; i<NF-1; i+=2) {
 | 
				
			||||||
 | 
					    if ($i == "TZID") {
 | 
				
			||||||
 | 
					      dt = "TZ=\"" $(i+1) "\" "
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  # Get date/date-time
 | 
				
			||||||
 | 
					  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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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 = substr(summary, index(summary, ":") + 1)
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# AWK program
 | 
				
			||||||
 | 
					BEGIN {
 | 
				
			||||||
 | 
					  FS="[:;=]"
 | 
				
			||||||
 | 
					  OFS="|"
 | 
				
			||||||
 | 
					  split(collection_labels, mapping, ";")
 | 
				
			||||||
 | 
					  for (map in mapping)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    split(mapping[map], m, "=")
 | 
				
			||||||
 | 
					    collection2label[m[1]] = m[2]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  # Colors
 | 
				
			||||||
 | 
					  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); nextfile }
 | 
				
			||||||
 | 
					/^DTSTART/ && inside  { start = parse() }
 | 
				
			||||||
 | 
					/^DTEND/ && inside    { end = parse() }
 | 
				
			||||||
 | 
					/^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,3 +1,9 @@
 | 
				
			|||||||
 | 
					## src/awk/calannot.awk
 | 
				
			||||||
 | 
					## Annotate monthly calendar
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## @assign cur: Day-of-month to mark as `today`
 | 
				
			||||||
 | 
					## @assign day: Day-of-month to highlight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BEGIN {
 | 
					BEGIN {
 | 
				
			||||||
  BLACK = "\033[1;30m"
 | 
					  BLACK = "\033[1;30m"
 | 
				
			||||||
  GREEN = "\033[1;32m"
 | 
					  GREEN = "\033[1;32m"
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/awk/calshift.awk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/awk/calshift.awk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					## 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,26 +1,69 @@
 | 
				
			|||||||
# 11:00|13:00|1748422800|1748430000|fpath|desc...
 | 
					## src/awk/dayview.awk
 | 
				
			||||||
# 00:00|00:00|1748296800|1748383200|fpath|desc...
 | 
					## Generate the view of a day from lines of the form
 | 
				
			||||||
function allday(desc) {
 | 
					## ```
 | 
				
			||||||
  return ITALIC FAINT "    (allday)    " OFF desc
 | 
					##  <start_date>|<end_date>|<start_time>|<end_time>|<file_path>|<collection>|<description>
 | 
				
			||||||
 | 
					## ```.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## @assign today: Date of `today` in the format %D (%m/%d/%y)
 | 
				
			||||||
 | 
					## @assign daystart: Hour of start of the day
 | 
				
			||||||
 | 
					## @assign dayend: Hour of end of the day
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Return line for all-day event.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @input collection: Collection symbol
 | 
				
			||||||
 | 
					# @input desc: Event description
 | 
				
			||||||
 | 
					# @return: Single-line string
 | 
				
			||||||
 | 
					function allday(collection, desc) {
 | 
				
			||||||
 | 
					  return collection " " ITALIC FAINT "    (allday)    " OFF desc
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function endstoday(stop, desc) {
 | 
					
 | 
				
			||||||
  return CYAN "      -- " stop OFF ": " desc
 | 
					# Return line for multi-day event, or event that starts at midnight, which ends today.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @input stop: Time at which the event ends
 | 
				
			||||||
 | 
					# @input collection: Collection symbol
 | 
				
			||||||
 | 
					# @input desc: Event description
 | 
				
			||||||
 | 
					# @return: Single-line string
 | 
				
			||||||
 | 
					function endstoday(stop, collection, desc) {
 | 
				
			||||||
 | 
					  return collection " " CYAN "      -- " stop OFF ": " desc
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function slice(start, stop, desc) {
 | 
					
 | 
				
			||||||
 | 
					# Return line for event that starts sometime today.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @input start: Time at which the event starts
 | 
				
			||||||
 | 
					# @input stop: Time at which the event ends
 | 
				
			||||||
 | 
					# @input collection: Collection symbol
 | 
				
			||||||
 | 
					# @input desc: Event description
 | 
				
			||||||
 | 
					# @return: Single-line string
 | 
				
			||||||
 | 
					function slice(start, stop, collection, desc) {
 | 
				
			||||||
  if (stop == "00:00")
 | 
					  if (stop == "00:00")
 | 
				
			||||||
    return CYAN start " --      " OFF ": " desc
 | 
					    return collection " " CYAN start " --      " OFF ": " desc
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    return CYAN start OFF " -- " CYAN stop OFF ": " desc
 | 
					    return collection " " 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 hour, "", "", "", FAINT hour ":00           ----------------------" OFF
 | 
					  print today, 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++)
 | 
					  for (i=h; i < starth + tmp && i < dayend; 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)
 | 
				
			||||||
@@ -28,23 +71,20 @@ function hrlines(start, stop, h,    starth, stoph, tmp, i) {
 | 
				
			|||||||
  else
 | 
					  else
 | 
				
			||||||
    return stoph + tmp
 | 
					    return stoph + tmp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# AWK program
 | 
				
			||||||
BEGIN {
 | 
					BEGIN {
 | 
				
			||||||
  FS = "|"
 | 
					  FS = "|"
 | 
				
			||||||
  GREEN = "\033[1;32m"
 | 
					 | 
				
			||||||
  RED = "\033[1;31m"
 | 
					 | 
				
			||||||
  WHITE = "\033[1;97m"
 | 
					 | 
				
			||||||
  CYAN = "\033[1;36m"
 | 
					  CYAN = "\033[1;36m"
 | 
				
			||||||
  ITALIC = "\033[3m"
 | 
					  ITALIC = "\033[3m"
 | 
				
			||||||
  FAINT = "\033[2m"
 | 
					  FAINT = "\033[2m"
 | 
				
			||||||
  OFF = "\033[m"
 | 
					  OFF = "\033[m"
 | 
				
			||||||
  OFS = "|"
 | 
					  OFS = "|"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
$1 == "00:00" && $2 == "00:00" { print $1, $3, $4, $5, allday($6);        next }
 | 
					$1 == "00:00" && $2 == "00:00" { print today, $1, $3, $4, $5, allday($6, $7);        next }
 | 
				
			||||||
$1 == "00:00"                  { print $1, $3, $4, $5, endstoday($2, $6); next }
 | 
					$1 == "00:00"                  { print today, $1, $3, $4, $5, endstoday($2, $6, $7); next }
 | 
				
			||||||
$1 ~ /^[0-9]{2}:[0-9]{2}$/     {
 | 
					$1 ~ /^[0-9]{2}:[0-9]{2}$/     {
 | 
				
			||||||
  daystart = hrlines($1, $2, daystart,    starth, stoph, tmp, i)
 | 
					  daystart = hrlines($1, $2, daystart)
 | 
				
			||||||
  print $1, $3, $4, $5, slice($1, $2, $6)
 | 
					  print today, $1, $3, $4, $5, slice($1, $2, $6, $7)
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
END {
 | 
					 | 
				
			||||||
  hrlines(dayend":00", 0, daystart,    starth, stoph, tmp, i)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					END                            { hrlines(dayend":00", 0, daystart)                        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,23 @@
 | 
				
			|||||||
# print content of field `field`
 | 
					## src/awk/get.awk
 | 
				
			||||||
BEGIN                     { FS = ":"; regex = "^" field; }
 | 
					## Print content of a field of an iCalendar file.
 | 
				
			||||||
/^BEGIN:VEVENT$/ { inside = 1 }
 | 
					##
 | 
				
			||||||
/^END:VEVENT$/ { exit }
 | 
					## @assign field: Field name
 | 
				
			||||||
$0 ~ regex                { content = $0;                    next; }
 | 
					
 | 
				
			||||||
/^ / && content           { content = content substr($0, 2); next; }
 | 
					# AWK program
 | 
				
			||||||
/^[^ ]/ && content        { exit }
 | 
					BEGIN              { FS = ":"; regex = "^" field }
 | 
				
			||||||
 | 
					/^BEGIN:VEVENT$/   { inside = 1 }
 | 
				
			||||||
 | 
					/^END:VEVENT$/     { exit }
 | 
				
			||||||
 | 
					$0 ~ regex         { content = $0; next }
 | 
				
			||||||
 | 
					/^ / && content    { content = content substr($0, 2); next }
 | 
				
			||||||
 | 
					/^[^ ]/ && content { exit }
 | 
				
			||||||
END {
 | 
					END {
 | 
				
			||||||
  if (!inside) { exit }
 | 
					  if (!inside) { exit }
 | 
				
			||||||
  # Process content line
 | 
					  # Process content line
 | 
				
			||||||
  content = substr(content, index(content, ":") + 1);
 | 
					  content = substr(content, index(content, ":") + 1)
 | 
				
			||||||
  gsub("\\\\n",    "\n", content);
 | 
					  gsub("\\\\n",    "\n", content)
 | 
				
			||||||
  gsub("\\\\N",    "\n", content);
 | 
					  gsub("\\\\N",    "\n", content)
 | 
				
			||||||
  gsub("\\\\,",    ",",  content);
 | 
					  gsub("\\\\,",    ",",  content)
 | 
				
			||||||
  gsub("\\\\;",    ";",  content);
 | 
					  gsub("\\\\;",    ";",  content)
 | 
				
			||||||
  gsub("\\\\\\\\", "\\", content);
 | 
					  gsub("\\\\\\\\", "\\", content)
 | 
				
			||||||
  print content;
 | 
					  print content
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,79 +0,0 @@
 | 
				
			|||||||
function parse(    dt) {
 | 
					 | 
				
			||||||
  # Get timezone information
 | 
					 | 
				
			||||||
  dt = "";
 | 
					 | 
				
			||||||
  for (i=2; i<NF-1; i+=2) {
 | 
					 | 
				
			||||||
    if ($i == "TZID") {
 | 
					 | 
				
			||||||
      dt = "TZ=\"" $(i+1) "\" ";
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  # Get date/datetime
 | 
					 | 
				
			||||||
  return length($NF) == 8 ?
 | 
					 | 
				
			||||||
    dt $NF :
 | 
					 | 
				
			||||||
    dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function parse_duration(    dt, dta, i, n, a, seps) {
 | 
					 | 
				
			||||||
  n = split($NF, a, /[PTWHMSD]/, seps);
 | 
					 | 
				
			||||||
  delete dta;
 | 
					 | 
				
			||||||
  for (i=2; i<=n; i++) {
 | 
					 | 
				
			||||||
    if(seps[i] == "W") dta["weeks"]   = a[i];
 | 
					 | 
				
			||||||
    if(seps[i] == "H") dta["hours"]   = a[i];
 | 
					 | 
				
			||||||
    if(seps[i] == "M") dta["minutes"] = a[i];
 | 
					 | 
				
			||||||
    if(seps[i] == "S") dta["seconds"] = a[i];
 | 
					 | 
				
			||||||
    if(seps[i] == "D") dta["days"]    = a[i];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  dt = a[1] ? a[1] : "+";
 | 
					 | 
				
			||||||
  for (i in dta) {
 | 
					 | 
				
			||||||
    dt = dt " " dta[i] " " i;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return dt;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function fn(path,    n, a) {
 | 
					 | 
				
			||||||
  n = split(path, a, "/");
 | 
					 | 
				
			||||||
  return a[n-1] "/" a[n];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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,17 +1,33 @@
 | 
				
			|||||||
BEGIN { FS="|"; i=0; dlt = -259200; spw = 604800; }
 | 
					## src/awk/merge.awk
 | 
				
			||||||
 | 
					## Merge a file that contains pairs of lines for start and end dates of events
 | 
				
			||||||
 | 
					## with the approximate data file, and group the iCalendar file paths according
 | 
				
			||||||
 | 
					## to the weeks at which the events take place.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# AWK program
 | 
				
			||||||
 | 
					BEGIN { FS="|" }
 | 
				
			||||||
NR == FNR {
 | 
					NR == FNR {
 | 
				
			||||||
  i = i + 1;
 | 
					  i = i + 1
 | 
				
			||||||
  from[i] = int(($1 + dlt)/ spw);
 | 
					  from_year[i] = $1
 | 
				
			||||||
  getline;
 | 
					  from_week[i] = $2
 | 
				
			||||||
  to[i] = int(($1 + dlt) / spw);
 | 
					  getline
 | 
				
			||||||
 | 
					  to_year[i] = $1
 | 
				
			||||||
 | 
					  to_week[i] = $2
 | 
				
			||||||
  next
 | 
					  next
 | 
				
			||||||
} # Load start and end week numbers from first file
 | 
					} # Load start and end week numbers from first file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{ 
 | 
					{ 
 | 
				
			||||||
  if (from[FNR] > to[FNR])
 | 
					  year_i = from_year[FNR]
 | 
				
			||||||
    print "FNR", FNR, ":", from[FNR],"-",to[FNR], "    ",$0;
 | 
					  week_i = from_week[FNR]
 | 
				
			||||||
  for(i=from[FNR]; i<=to[FNR]; i++) {
 | 
					  year_end = to_year[FNR]
 | 
				
			||||||
    week[i] = week[i] " " $5
 | 
					  week_end = to_week[FNR]
 | 
				
			||||||
 | 
					  while(year_i <= year_end && (year_i < year_end || week_i <= week_end)) {
 | 
				
			||||||
 | 
					    label = year_i"|"week_i
 | 
				
			||||||
 | 
					    week[label] = week[label] " " $5
 | 
				
			||||||
 | 
					    week_i++
 | 
				
			||||||
 | 
					    if (week_i > 53) {
 | 
				
			||||||
 | 
					      week_i = 1
 | 
				
			||||||
 | 
					      year_i++
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
END { for (i in week) print i week[i]; }
 | 
					END { for (label in week) print label week[label] }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										141
									
								
								src/awk/new.awk
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								src/awk/new.awk
									
									
									
									
									
								
							@@ -1,95 +1,140 @@
 | 
				
			|||||||
 | 
					## src/awk/new.awk
 | 
				
			||||||
 | 
					## Generate iCalendar file from markdown description.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## @assign uid: UID to use
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Escape string to be used as content in iCalendar files.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @input str: String to escape
 | 
				
			||||||
 | 
					# @return: Escaped string
 | 
				
			||||||
function escape(str)
 | 
					function escape(str)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  gsub("\\\\", "\\\\", str);
 | 
					  gsub("\\\\", "\\\\", str)
 | 
				
			||||||
  gsub(";",  "\\\\;",    str);
 | 
					  gsub(";",    "\\;",  str)
 | 
				
			||||||
  gsub(",",  "\\\\,",    str);
 | 
					  gsub(",",    "\\,",  str)
 | 
				
			||||||
 | 
					  return str
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Print property with its content and fold according to the iCalendar
 | 
				
			||||||
 | 
					# specification.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @local variables: i, s
 | 
				
			||||||
 | 
					# @input nameparam: Property name with optional parameters
 | 
				
			||||||
 | 
					# @input content: Escaped content
 | 
				
			||||||
function print_fold(nameparam, content,    i, s)
 | 
					function print_fold(nameparam, content,    i, s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  i = 74 - length(nameparam);
 | 
					  i = 74 - length(nameparam)
 | 
				
			||||||
  s = substr(content, 1, i);
 | 
					  s = substr(content, 1, i)
 | 
				
			||||||
  print nameparam s;
 | 
					  print nameparam s
 | 
				
			||||||
  s = substr(content, i+1, 73);
 | 
					  s = substr(content, i+1, 73)
 | 
				
			||||||
  i = i + 73;
 | 
					  i = i + 73
 | 
				
			||||||
  while (s)
 | 
					  while (s)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    print " " s;
 | 
					    print " " s
 | 
				
			||||||
    s = substr(content, i+1, 73);
 | 
					    s = substr(content, i+1, 73)
 | 
				
			||||||
    i = i + 73;
 | 
					    i = i + 73
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# AWK program
 | 
				
			||||||
BEGIN { 
 | 
					BEGIN { 
 | 
				
			||||||
  FS=":"; 
 | 
					  FS=":"
 | 
				
			||||||
  zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1);
 | 
					  zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
desc { desc = desc "\\n" $0; next; }
 | 
					desc { desc = desc "\\n" $0; next }
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : "";
 | 
					  from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""
 | 
				
			||||||
 | 
					  if (!from)
 | 
				
			||||||
 | 
					    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) == "@ " ? substr($0, 3) : ""
 | 
				
			||||||
 | 
					  if (location) getline
 | 
				
			||||||
  summary = substr($0, 1, 2) == "# " ? substr($0, 3) : ""
 | 
					  summary = substr($0, 1, 2) == "# " ? substr($0, 3) : ""
 | 
				
			||||||
 | 
					  if (!summary)
 | 
				
			||||||
 | 
					    exit 1
 | 
				
			||||||
  getline # This line should be empty
 | 
					  getline # This line should be empty
 | 
				
			||||||
  getline # First line of description
 | 
					  getline # First line of description
 | 
				
			||||||
  desc = $0;
 | 
					  desc = $0
 | 
				
			||||||
  next;
 | 
					  next
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
END {
 | 
					END {
 | 
				
			||||||
  # Sanitize input
 | 
					  # Sanitize input
 | 
				
			||||||
  # If nanoseconds are not 0, then we assume user enterd "tomorrow" or
 | 
					  # If nanoseconds are not 0, then we assume user entered "tomorrow" or
 | 
				
			||||||
  # something the like, and we make this a date entry, as opposed to a
 | 
					  # something the like, and we make this a date entry, as opposed to a
 | 
				
			||||||
  # date-time entry.
 | 
					  # date-time entry.
 | 
				
			||||||
  from = from ? from : "now"
 | 
					  # Similarly, if the time is 00:00, we make this a date, as opposed to a
 | 
				
			||||||
  cmd = "date -d \"" from "\" +\"%N\"";
 | 
					  # date-time entry.
 | 
				
			||||||
 | 
					  gsub("\"", "\\\"", from)
 | 
				
			||||||
 | 
					  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 (t == 0) {
 | 
					  if (n != 0 || 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\"";
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  cmd | getline from
 | 
					  suc = cmd | getline from
 | 
				
			||||||
  close(cmd)
 | 
					  close(cmd)
 | 
				
			||||||
 | 
					  if (suc != 1) {
 | 
				
			||||||
 | 
					    exit 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  #
 | 
					  #
 | 
				
			||||||
  to = to ? to : "now"
 | 
					  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 (t == 0) {
 | 
					  if (n != 0 || 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\"";
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  cmd | getline to
 | 
					  suc = cmd | getline to
 | 
				
			||||||
  close(cmd)
 | 
					  close(cmd)
 | 
				
			||||||
  escape(summary);
 | 
					  if (suc != 1) {
 | 
				
			||||||
  escape(desc);
 | 
					    exit 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  summary = escape(summary)
 | 
				
			||||||
 | 
					  location = escape(location)
 | 
				
			||||||
 | 
					  desc = escape(desc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # print ical
 | 
					  # print ical
 | 
				
			||||||
  print "BEGIN:VCALENDAR";
 | 
					  print "BEGIN:VCALENDAR"
 | 
				
			||||||
  print "VERSION:2.0";
 | 
					  print "VERSION:2.0"
 | 
				
			||||||
  print "CALSCALE:GREGORIAN";
 | 
					  print "CALSCALE:GREGORIAN"
 | 
				
			||||||
  print "PRODID:-//fab//awk//EN";
 | 
					  print "PRODID:-//fab//awk//EN"
 | 
				
			||||||
  print "BEGIN:VEVENT"
 | 
					  print "BEGIN:VEVENT"
 | 
				
			||||||
  print "DTSTAMP:" zulu;
 | 
					  print "DTSTAMP:" zulu
 | 
				
			||||||
  print "UID:" uid;
 | 
					  print "UID:" uid
 | 
				
			||||||
  print "CLASS:PRIVATE";
 | 
					  print "CLASS:PRIVATE"
 | 
				
			||||||
  print "CREATED:" zulu;
 | 
					  print "CREATED:" zulu
 | 
				
			||||||
  print "SEQUENCE:1";
 | 
					  print "SEQUENCE:1"
 | 
				
			||||||
  print "LAST-MODIFIED:" zulu;
 | 
					  print "LAST-MODIFIED:" zulu
 | 
				
			||||||
  print "STATUS:FINAL";
 | 
					  print "STATUS:VEVENT"
 | 
				
			||||||
  print "DTSTART;VALUE=" from_type ":" from
 | 
					  print "DTSTART;VALUE=" from_type ":" from
 | 
				
			||||||
  print "DTEND;VALUE=" to_type ":" to
 | 
					  print "DTEND;VALUE=" to_type ":" to
 | 
				
			||||||
  if (summary)    print_fold("SUMMARY:",     summary,       i, s);
 | 
					  if (summary)    print_fold("SUMMARY:",     summary)
 | 
				
			||||||
  if (desc)       print_fold("DESCRIPTION:", desc,          i, s);
 | 
					  if (desc)       print_fold("DESCRIPTION:", desc)
 | 
				
			||||||
 | 
					  if (location)   print_fold("LOCATION:",    location)
 | 
				
			||||||
  print "END:VEVENT"
 | 
					  print "END:VEVENT"
 | 
				
			||||||
  print "END:VCALENDAR"
 | 
					  print "END:VCALENDAR"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,46 +1,69 @@
 | 
				
			|||||||
 | 
					## src/awk/parse.awk
 | 
				
			||||||
 | 
					## Parse iCalendar file and print its key aspects:
 | 
				
			||||||
 | 
					## ```
 | 
				
			||||||
 | 
					## <start> <end> <fpath> <collection> <summary>
 | 
				
			||||||
 | 
					## ```.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## @assign collection_labels: See configuration of the current program.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Time-zone aware parsing of the date/date-time entry at the current record.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @local variables: dt
 | 
				
			||||||
 | 
					# @return: date or date-time string that can be used in date (1)
 | 
				
			||||||
function parse(    dt) {
 | 
					function parse(    dt) {
 | 
				
			||||||
  # Get timezone information
 | 
					  # Get timezone information
 | 
				
			||||||
  dt = "";
 | 
					 | 
				
			||||||
  for (i=2; i<NF-1; i+=2) {
 | 
					  for (i=2; i<NF-1; i+=2) {
 | 
				
			||||||
    if ($i == "TZID") {
 | 
					    if ($i == "TZID") {
 | 
				
			||||||
      dt = "TZ=\"" $(i+1) "\" ";
 | 
					      dt = "TZ=\"" $(i+1) "\" "
 | 
				
			||||||
      break;
 | 
					      break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  # Get date/datetime
 | 
					  # Get date/date-time
 | 
				
			||||||
  return length($NF) == 8 ?
 | 
					  return length($NF) == 8 ?
 | 
				
			||||||
    dt $NF :
 | 
					    dt $NF :
 | 
				
			||||||
    dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF);
 | 
					    dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parse_duration(    dt, dta, i, n, a, seps) {
 | 
					# Map iCalendar duration specification into the format to be used in date (1).
 | 
				
			||||||
  n = split($NF, a, /[PTWHMSD]/, seps);
 | 
					#
 | 
				
			||||||
  delete dta;
 | 
					# @local variables: dt, dta, i, n, a, seps
 | 
				
			||||||
 | 
					# @input duration: iCalendar duration string
 | 
				
			||||||
 | 
					# @return: relative-date/date-time specification to be used in date (1)
 | 
				
			||||||
 | 
					function parse_duration(duration,    dt, dta, i, n, a, seps) {
 | 
				
			||||||
 | 
					  n = split(duration, a, /[PTWHMSD]/, seps)
 | 
				
			||||||
  for (i=2; i<=n; i++) {
 | 
					  for (i=2; i<=n; i++) {
 | 
				
			||||||
    if(seps[i] == "W") dta["weeks"]   = a[i];
 | 
					    if(seps[i] == "W") dta["weeks"]   = a[i]
 | 
				
			||||||
    if(seps[i] == "H") dta["hours"]   = a[i];
 | 
					    if(seps[i] == "H") dta["hours"]   = a[i]
 | 
				
			||||||
    if(seps[i] == "M") dta["minutes"] = a[i];
 | 
					    if(seps[i] == "M") dta["minutes"] = a[i]
 | 
				
			||||||
    if(seps[i] == "S") dta["seconds"] = a[i];
 | 
					    if(seps[i] == "S") dta["seconds"] = a[i]
 | 
				
			||||||
    if(seps[i] == "D") dta["days"]    = a[i];
 | 
					    if(seps[i] == "D") dta["days"]    = a[i]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  dt = a[1] ? a[1] : "+";
 | 
					  dt = a[1] ? a[1] : "+"
 | 
				
			||||||
  for (i in dta) {
 | 
					  for (i in dta)
 | 
				
			||||||
    dt = dt " " dta[i] " " i;
 | 
					    dt = dt " " dta[i] " " i
 | 
				
			||||||
  }
 | 
					  return dt
 | 
				
			||||||
  return dt;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function print_data(start, dur, end, summary,    cmd, collection) {
 | 
					# Print string of parsed data.
 | 
				
			||||||
  summary = substr(summary, index(summary, ":") + 1);
 | 
					#
 | 
				
			||||||
  gsub("\\\\n",    " ", summary); # one-liner
 | 
					# @local variables: cmd, collection, depth, path
 | 
				
			||||||
  gsub("\\\\N",    " ", summary); # one-liner
 | 
					# @input start: Start time of event
 | 
				
			||||||
  gsub("\\\\,",    ",",  summary);
 | 
					# @input dur: Boolean that indicates that `end` specifies a duration
 | 
				
			||||||
  gsub("\\\\;",    ";",  summary);
 | 
					# @input end: End time of event, or event duration (see `dur`)
 | 
				
			||||||
  gsub("\\\\\\\\", "\\", summary);
 | 
					# @input summary: Content of SUMMARY field of the event
 | 
				
			||||||
  depth = split(FILENAME, path, "/");
 | 
					function print_data(start, dur, end, summary,    cmd, collection, depth, path) {
 | 
				
			||||||
 | 
					  summary = substr(summary, index(summary, ":") + 1)
 | 
				
			||||||
 | 
					  gsub("\\\\n",    " ",  summary) # one-liner
 | 
				
			||||||
 | 
					  gsub("\\\\N",    " ",  summary) # one-liner
 | 
				
			||||||
 | 
					  gsub("\\\\,",    ",",  summary)
 | 
				
			||||||
 | 
					  gsub("\\\\;",    ";",  summary)
 | 
				
			||||||
 | 
					  gsub("\\\\\\\\", "\\", summary)
 | 
				
			||||||
 | 
					  depth = split(FILENAME, path, "/")
 | 
				
			||||||
  fpath = path[depth-1] "/" path[depth]
 | 
					  fpath = path[depth-1] "/" path[depth]
 | 
				
			||||||
  collection = depth > 1 ? path[depth-1] : "";
 | 
					  collection = depth > 1 ? path[depth-1] : ""
 | 
				
			||||||
  collection = collection in collection2label ? collection2label[collection] : collection;
 | 
					  collection = collection in collection2label ? collection2label[collection] : collection
 | 
				
			||||||
  collection = collection2label[path[depth-1]]
 | 
					  collection = collection2label[path[depth-1]]
 | 
				
			||||||
  end = dur ? start " " end : end
 | 
					  end = dur ? start " " end : end
 | 
				
			||||||
  cmd = "date -d '" start "' +\"%s\""
 | 
					  cmd = "date -d '" start "' +\"%s\""
 | 
				
			||||||
@@ -52,20 +75,21 @@ function print_data(start, dur, end, summary,    cmd, collection) {
 | 
				
			|||||||
  print start, end, fpath, collection, summary
 | 
					  print start, end, fpath, collection, summary
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BEGIN                 { 
 | 
					# AWK program
 | 
				
			||||||
  FS="[:;=]";
 | 
					BEGIN { 
 | 
				
			||||||
  split(collection_labels, mapping, ";");
 | 
					  FS="[:;=]"
 | 
				
			||||||
 | 
					  split(collection_labels, mapping, ";")
 | 
				
			||||||
  for (map in mapping)
 | 
					  for (map in mapping)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    split(mapping[map], m, "=");
 | 
					    split(mapping[map], m, "=")
 | 
				
			||||||
    collection2label[m[1]] = m[2];
 | 
					    collection2label[m[1]] = m[2]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/^END:VEVENT/ && inside { print_data(start, dur, end, summary,    cmd, collection); exit }
 | 
					/^END:VEVENT/ && inside { print_data(start, dur, end, summary); exit }
 | 
				
			||||||
/^DTSTART/ && inside  { start = parse(    dt) }
 | 
					/^DTSTART/ && inside    { start = parse() }
 | 
				
			||||||
/^DTEND/ && inside    { end = parse(    dt) }
 | 
					/^DTEND/ && inside      { end = parse() }
 | 
				
			||||||
/^DURATION/ && inside { end = parse_duration(    dt, dta, i, n, a, seps); dur = 1 }
 | 
					/^DURATION/ && inside   { end = parse_duration($NF); dur = 1 }
 | 
				
			||||||
/^[^ ]/ && rs { rs = 0 }
 | 
					/^[^ ]/ && rs           { rs = 0 }
 | 
				
			||||||
/^ / && rs       { summary = summary substr($0, 2); }
 | 
					/^ / && rs              { summary = summary substr($0, 2) }
 | 
				
			||||||
/^SUMMARY/ && inside  { rs = 1; summary = $0; }
 | 
					/^SUMMARY/ && inside    { rs = 1; summary = $0 }
 | 
				
			||||||
/^BEGIN:VEVENT/ { inside = 1 }
 | 
					/^BEGIN:VEVENT/         { inside = 1 }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										33
									
								
								src/awk/set.awk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/awk/set.awk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					## src/awk/set.awk
 | 
				
			||||||
 | 
					## Set or replace the content of a specified field in the iCalendar file.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## @assign field: iCalendar field
 | 
				
			||||||
 | 
					## @assign value: Content to set it to
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## LIMITATION: This program does not fold long content lines.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Escape string to be used as content.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @input str: Content string
 | 
				
			||||||
 | 
					# @return: Escaped string
 | 
				
			||||||
 | 
					function escape(str)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  gsub("\\\\", "\\\\", str)
 | 
				
			||||||
 | 
					  gsub(";",    "\\;",  str)
 | 
				
			||||||
 | 
					  gsub(",",    "\\,",  str)
 | 
				
			||||||
 | 
					  return str
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# AWK program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BEGIN                 { FS = "[:;]" }
 | 
				
			||||||
 | 
					/^BEGIN:VEVENT$/      { inside = 1 }
 | 
				
			||||||
 | 
					/^END:VEVENT$/        { inside = 0; if (!duplic) print field ":" escape(value) }
 | 
				
			||||||
 | 
					$1 == field && inside { con = 1; duplic = 1; print field ":" escape(value); next }
 | 
				
			||||||
 | 
					$1 == field && duplic { con = 1; next }
 | 
				
			||||||
 | 
					/^ / && con           { next }
 | 
				
			||||||
 | 
					/^ / && con           { next }
 | 
				
			||||||
 | 
					/^[^ ]/ && con        { con = 0 }
 | 
				
			||||||
 | 
					{ print }
 | 
				
			||||||
@@ -1,101 +1,138 @@
 | 
				
			|||||||
function getcontent(content_line, prop)
 | 
					## src/awk/update.awk
 | 
				
			||||||
{
 | 
					## Update iCalendar file from markdown file.
 | 
				
			||||||
  return substr(content_line[prop], index(content_line[prop], ":") + 1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Escape string to be used as content in iCalendar files.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @input str: String to escape
 | 
				
			||||||
 | 
					# @return: Escaped string
 | 
				
			||||||
function escape(str)
 | 
					function escape(str)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  gsub("\\\\", "\\\\", str);
 | 
					  gsub("\\\\", "\\\\", str)
 | 
				
			||||||
  gsub(";",  "\\\\;",    str);
 | 
					  gsub(";",    "\\;",  str)
 | 
				
			||||||
  gsub(",",  "\\\\,",    str);
 | 
					  gsub(",",    "\\,",  str)
 | 
				
			||||||
 | 
					  return str
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Print property with its content and fold according to the iCalendar
 | 
				
			||||||
 | 
					# specification.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @local variables: i, s
 | 
				
			||||||
 | 
					# @input nameparam: Property name with optional parameters
 | 
				
			||||||
 | 
					# @input content: Escaped content
 | 
				
			||||||
function print_fold(nameparam, content,    i, s)
 | 
					function print_fold(nameparam, content,    i, s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  i = 74 - length(nameparam);
 | 
					  i = 74 - length(nameparam)
 | 
				
			||||||
  s = substr(content, 1, i);
 | 
					  s = substr(content, 1, i)
 | 
				
			||||||
  print nameparam s;
 | 
					  print nameparam s
 | 
				
			||||||
  s = substr(content, i+1, 73);
 | 
					  s = substr(content, i+1, 73)
 | 
				
			||||||
  i = i + 73;
 | 
					  i = i + 73
 | 
				
			||||||
  while (s)
 | 
					  while (s)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    print " " s;
 | 
					    print " " s
 | 
				
			||||||
    s = substr(content, i+1, 73);
 | 
					    s = substr(content, i+1, 73)
 | 
				
			||||||
    i = i + 73;
 | 
					    i = i + 73
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# AWK program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BEGIN { 
 | 
					BEGIN { 
 | 
				
			||||||
  FS=":";
 | 
					  FS=":"
 | 
				
			||||||
  zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1);
 | 
					  zulu = strftime("%Y%m%dT%H%M%SZ", systime(), 1)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENDFILE {
 | 
					ENDFILE {
 | 
				
			||||||
  if (NR == FNR)
 | 
					  if (NR == FNR)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    # If nanoseconds are not 0, then we assume user enterd "tomorrow" or
 | 
					    # If nanoseconds are not 0, then we assume user entered "tomorrow" or
 | 
				
			||||||
    # something the like, and we make this a date entry, as opposed to a
 | 
					    # something the like, and we make this a date entry, as opposed to a
 | 
				
			||||||
    # date-time entry.
 | 
					    # date-time entry.
 | 
				
			||||||
    from = from ? from : "now"
 | 
					    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 (t == 0) {
 | 
					    if (n != 0 || 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\"";
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    cmd | getline from
 | 
					    suc = cmd | getline from
 | 
				
			||||||
    close(cmd)
 | 
					    close(cmd)
 | 
				
			||||||
 | 
					    if (suc != 1) {
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    to = to ? to : "now"
 | 
					    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 (t == 0) {
 | 
					    if (n != 0 || 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\"";
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    cmd | getline to
 | 
					    suc = cmd | getline to
 | 
				
			||||||
    close(cmd)
 | 
					    close(cmd)
 | 
				
			||||||
 | 
					    if (suc != 1) {
 | 
				
			||||||
 | 
					      exit 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    summary = escape(summary)
 | 
				
			||||||
 | 
					    location = escape(location)
 | 
				
			||||||
 | 
					    desc = escape(desc)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  escape(summary);
 | 
					 | 
				
			||||||
  escape(desc);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NR == FNR && desc { desc = desc "\\n" $0; next; }
 | 
					NR == FNR && desc { desc = desc "\\n" $0; next }
 | 
				
			||||||
NR == FNR {
 | 
					NR == FNR {
 | 
				
			||||||
  from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : "";
 | 
					  from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""
 | 
				
			||||||
 | 
					  if (!from)
 | 
				
			||||||
 | 
					    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) == "@ " ? substr($0, 3) : ""
 | 
				
			||||||
 | 
					  if (location) getline
 | 
				
			||||||
  summary = substr($0, 1, 2) == "# " ? substr($0, 3) : ""
 | 
					  summary = substr($0, 1, 2) == "# " ? substr($0, 3) : ""
 | 
				
			||||||
 | 
					  if (!summary)
 | 
				
			||||||
 | 
					    exit 1
 | 
				
			||||||
  getline # This line should be empty
 | 
					  getline # This line should be empty
 | 
				
			||||||
  getline # First line of description
 | 
					  getline # First line of description
 | 
				
			||||||
  desc = $0;
 | 
					  desc = $0
 | 
				
			||||||
  next;
 | 
					  next
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/^BEGIN:VEVENT$/                                              { inside = 1; print; next }
 | 
					/^BEGIN:VEVENT$/        { inside = 1; print; next }
 | 
				
			||||||
/^X-ALT-DESC/ && inside                                       { next } # drop this alternative description
 | 
					/^X-ALT-DESC/ && inside { next } # drop this alternative description
 | 
				
			||||||
/^ / && inside                                                { next } # drop this folded line (the only content with folded lines will be updated)
 | 
					/^ / && 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
 | 
					/^(DTSTART|DTEND|SUMMARY|LOCATION|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
 | 
					/^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,       i, s)
 | 
					  print_fold("SUMMARY:",     summary)
 | 
				
			||||||
  print_fold("DESCRIPTION:", desc,          i, s)
 | 
					  print_fold("DESCRIPTION:", desc)
 | 
				
			||||||
 | 
					  print_fold("LOCATION:",    location)
 | 
				
			||||||
  inside = ""
 | 
					  inside = ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
{ print }
 | 
					{ print }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,44 +0,0 @@
 | 
				
			|||||||
function parse(    dt) {
 | 
					 | 
				
			||||||
  # Get timezone information
 | 
					 | 
				
			||||||
  dt = "";
 | 
					 | 
				
			||||||
  for (i=2; i<NF-1; i+=2) {
 | 
					 | 
				
			||||||
    if ($i == "TZID") {
 | 
					 | 
				
			||||||
      dt = "TZ=\"" $(i+1) "\" ";
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  # Get date/datetime
 | 
					 | 
				
			||||||
  return length($NF) == 8 ?
 | 
					 | 
				
			||||||
    dt $NF :
 | 
					 | 
				
			||||||
    dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function parse_duration(    dt, dta, i, n, a, seps) {
 | 
					 | 
				
			||||||
  n = split($NF, a, /[PTWHMSD]/, seps);
 | 
					 | 
				
			||||||
  delete dta;
 | 
					 | 
				
			||||||
  for (i=2; i<=n; i++) {
 | 
					 | 
				
			||||||
    if(seps[i] == "W") dta["weeks"]   = a[i];
 | 
					 | 
				
			||||||
    if(seps[i] == "H") dta["hours"]   = a[i];
 | 
					 | 
				
			||||||
    if(seps[i] == "M") dta["minutes"] = a[i];
 | 
					 | 
				
			||||||
    if(seps[i] == "S") dta["seconds"] = a[i];
 | 
					 | 
				
			||||||
    if(seps[i] == "D") dta["days"]    = a[i];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  dt = a[1] ? a[1] : "+";
 | 
					 | 
				
			||||||
  for (i in dta) {
 | 
					 | 
				
			||||||
    dt = dt " " dta[i] " " i;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return dt;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function fn(path,    n, a) {
 | 
					 | 
				
			||||||
  n = split(path, a, "/");
 | 
					 | 
				
			||||||
  return a[n-1] "/" a[n];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BEGIN                 { FS="[:;=]"; OFS="|" }
 | 
					 | 
				
			||||||
BEGINFILE             { inside = 0; dur = 0; start = "ERROR"; end = "ERROR" }
 | 
					 | 
				
			||||||
/^END:VEVENT/         { print start, dur ? start " " end : end, fn(FILENAME,    n, a); nextfile }
 | 
					 | 
				
			||||||
/^DTSTART/ && inside  { start = parse(    dt) }
 | 
					 | 
				
			||||||
/^DTEND/ && inside    { end = parse(    dt) }
 | 
					 | 
				
			||||||
/^DURATION/ && inside { end = parse_duration(    dt, dta, i, n, a, seps); dur = 1 }
 | 
					 | 
				
			||||||
/^BEGIN:VEVENT/       { inside = 1 }
 | 
					 | 
				
			||||||
@@ -1,23 +1,34 @@
 | 
				
			|||||||
 | 
					## src/awk/weekview.awk
 | 
				
			||||||
 | 
					## Print view of all appointments of the current week.
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					## @assign startofweek: Date of first day in the week
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Compose line that will display a day in the week.
 | 
				
			||||||
 | 
					# 
 | 
				
			||||||
 | 
					# @return: Single-line string
 | 
				
			||||||
function c() {
 | 
					function c() {
 | 
				
			||||||
  return CYAN substr($0, index($0, ">") + 1) OFF "  " RED "/" OFF
 | 
					  return CYAN substr($0, index($0, ">") + 1) OFF "  " RED "/" OFF
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# AWK program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BEGIN {
 | 
					BEGIN {
 | 
				
			||||||
  GREEN = "\033[1;32m";
 | 
					  GREEN = "\033[1;32m"
 | 
				
			||||||
  RED = "\033[1;31m";
 | 
					  RED = "\033[1;31m"
 | 
				
			||||||
  WHITE = "\033[1;97m";
 | 
					  CYAN = "\033[1;36m"
 | 
				
			||||||
  CYAN = "\033[1;36m";
 | 
					  OFF = "\033[m"
 | 
				
			||||||
  FAINT = "\033[2m";
 | 
					 | 
				
			||||||
  OFF = "\033[m";
 | 
					 | 
				
			||||||
  OFS = "|"
 | 
					  OFS = "|"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/^[0-7] 00:00 -- 00:00/ { dayline = dayline " " c(); next }
 | 
					/^[0-7] 00:00 -- 00:00/                         { dayline = dayline " " c(); next }
 | 
				
			||||||
/^[0-7] 00:00 -- / { dayline = dayline " <--" $4 " " c(); next }
 | 
					/^[0-7] 00:00 -- /                              { dayline = dayline " <--" $4 " " c(); next }
 | 
				
			||||||
/^[0-7] [0-9]{2}:[0-9]{2} -- 00:00/ { dayline = dayline " " $2 "--> " c(); next }
 | 
					/^[0-7] [0-9]{2}:[0-9]{2} -- 00:00/             { dayline = dayline " " $2 "→" c(); next }
 | 
				
			||||||
/^[0-7] [0-9]{2}:[0-9]{2} -- [0-9]{2}:[0-9]{2}/ { dayline = dayline " " $2 " - " $4 " " c(); next }
 | 
					/^[0-7] [0-9]{2}:[0-9]{2} -- [0-9]{2}:[0-9]{2}/ { dayline = dayline " " $2 " - " $4 " " c(); next }
 | 
				
			||||||
/^[0-7]$/ && dayline { print "+", startofweek " +" $0-1 " days", "", dayline; }
 | 
					/^[0-7]$/ && dayline                            { print "+", startofweek " +" $0-1 " days", "", dayline }
 | 
				
			||||||
/^[0-7]$/ {
 | 
					/^[0-7]$/ {
 | 
				
			||||||
  cmd = "date -d '" startofweek " +" $0 " days' +\"%a %e %b %Y\"";
 | 
					  cmd = "date -d '" startofweek " +" $0 " days' +\"%a %e %b %Y\""
 | 
				
			||||||
  cmd | getline dayline;
 | 
					  cmd | getline dayline
 | 
				
			||||||
  close(cmd);
 | 
					  close(cmd)
 | 
				
			||||||
  dayline = GREEN dayline ":   " OFF
 | 
					  dayline = GREEN dayline ":   " OFF
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1471
									
								
								src/main.sh
									
									
									
									
									
								
							
							
						
						
									
										1471
									
								
								src/main.sh
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user