├── .gitignore └── ical2org.awk /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | *.egg-info 3 | -------------------------------------------------------------------------------- /ical2org.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S gawk -f 2 | # awk script for converting an iCal formatted file to a sequence of org-mode headings. 3 | # this may not work in general but seems to work for day and timed events from Google's 4 | # calendar, which is really all I need right now... 5 | # 6 | # usage: 7 | # awk -f THISFILE < icalinputfile.ics > orgmodeentries.org 8 | # 9 | # Note: change org meta information generated below for author and 10 | # email entries! 11 | # 12 | # Caveats: 13 | # 14 | # - date entries with no time specified are assumed to be local time zone; 15 | # same remark for date entries that do have a time but do not end with Z 16 | # e.g.: 20130101T123456 is local and will be kept as 2013-01-01 12:34 17 | # where 20130223T123422Z is UTC and will be corrected appropriately 18 | # 19 | # - UTC times are changed into local times, using the time zone of the 20 | # computer that runs the script; it would be very hard in an awk script 21 | # to respect the time zone of a file belonging to another time zone: 22 | # the offsets will be different as well as the switchover time(s); 23 | # (consider a remote shell to a computer with the file's time zone) 24 | # 25 | # - the UTC conversion entirely relies on the built-in strftime method; 26 | # the author is not responsible for any erroneous conversions nor the 27 | # consequence of such conversions 28 | # 29 | # - does process RRULE recurring events, but ignores COUNT specifiers 30 | # 31 | # - does not process EXDATE to exclude date(s) from recurring events 32 | # 33 | # Eric S Fraga 34 | # 20100629 - initial version 35 | # 20100708 - added end times to timed events 36 | # - adjust times according to time zone information 37 | # - fixed incorrect transfer for entries with ":" embedded within the text 38 | # - added support for multi-line summary entries (which become headlines) 39 | # 20100709 - incorporated time zone identification 40 | # - fixed processing of continuation lines as Google seems to 41 | # have changed, in the last day, the number of spaces at 42 | # the start of the line for each continuation... 43 | # - remove backslashes used to protect commas in iCal text entries 44 | # no further revision log after this as the file was moved into a git 45 | # repository... 46 | # 47 | # Updated by: Guido Van Hoecke 48 | # Last change: 2013.05.26 14:28:33 49 | #---------------------------------------------------------------------------------- 50 | 51 | BEGIN { 52 | ### config section 53 | 54 | attending_types["UNSET"] = 0; 55 | attending_types["ATTENDING"] = 1; 56 | attending_types["NEEDS_ACTION"] = 2; 57 | attending_types["NOT_ATTENDING"] = 3; 58 | attending_types[0] = "UNSET"; 59 | attending_types[1] = "ATTENDING"; 60 | attending_types[2] = "NEEDS_ACTION"; 61 | attending_types[3] = "NOT_ATTENDING"; 62 | 63 | # map of UIDS for duplicate checking -- sometimes the same id comes down 64 | # with multiple VEVENTS 65 | UIDS[0]; 66 | 67 | # map of people attending a given event 68 | people_attending[0]; 69 | 70 | # maximum age in days for entries to be output: set this to -1 to 71 | # get all entries or to N>0 to only get enties that start or end 72 | # less than N days ago 73 | max_age = 7; 74 | 75 | # set to 1 or 0 to yes or not output a header block with TITLE, 76 | # AUTHOR, EMAIL etc... 77 | header = 1; 78 | 79 | # set to 1 or 0 to yes or not output the original ical preamble as 80 | # comment 81 | preamble = 1; 82 | 83 | # set to 1 to output time and summary as one line starting with 84 | # the time (value 1) or to 0 to output the summary as first line 85 | # and the date and time info as a later line (after the property 86 | # drawer or org complains) 87 | condense = 0; 88 | 89 | # set to 1 or 0 to yes or not output the original ical entry as a 90 | # comment (mostly useful for debugging purposes) 91 | original = 1; 92 | 93 | # google truncates long subjects with ... which is misleading in 94 | # an org file: it gives the unfortunate impression that an 95 | # expanded entry is still collapsed; value 1 will trim those 96 | # ... and value 0 doesn't touch them 97 | trimdots = 1; 98 | 99 | # change this to your name 100 | author = ENVIRON["AUTHOR"] != "" ? ENVIRON["AUTHOR"] : "Marc Sherry" 101 | 102 | # and to your email address 103 | emailaddress = ENVIRON["EMAIL"] != "" ? ENVIRON["EMAIL"] : "unknown" 104 | 105 | # main title of the Org file 106 | title = ENVIRON["TITLE"] != "" ? ENVIRON["TITLE"] : "Main Google calendar entries" 107 | 108 | # calendar/category name for display in org-mode 109 | calendarname = ENVIRON["CALENDAR"] != "" ? ENVIRON["CALENDAR"] : "unknown" 110 | 111 | # any tags for this calendar (e.g. "WORK" or "PERSONAL") 112 | filetags = ENVIRON["FILETAGS"] != "" ? ENVIRON["FILETAGS"] : "unknown" 113 | 114 | # timezone offsets 115 | "date +%z" | getline local_tz_offset 116 | close("date +%z") 117 | local_tz_offset = parse_timezone_offset(local_tz_offset) 118 | 119 | ### end config section 120 | 121 | # use a colon to separate the type of data line from the actual contents 122 | FS = ":"; 123 | 124 | # we only need to preserve the original entry lines if either the 125 | # preamble or original options are true 126 | preserve = preamble || original 127 | first = 1; # true until an event has been found 128 | max_age_seconds = max_age*24*60*60 129 | 130 | if (header) { 131 | print "#+TITLE: ", title 132 | print "#+AUTHOR: ", author 133 | print "#+EMAIL: ", emailaddress 134 | print "#+DESCRIPTION: converted using the ical2org awk script" 135 | print "#+CATEGORY: ", calendarname 136 | print "#+STARTUP: hidestars" 137 | print "#+STARTUP: overview" 138 | print "#+FILETAGS: ", filetags 139 | print "" 140 | } 141 | } 142 | 143 | # continuation lines (at least from Google) start with a space. If the 144 | # continuation is after a processed field (description, summary, attendee, 145 | # etc.) append the entry to the respective variable 146 | /^[ ]/ { 147 | if (indescription) { 148 | entry = entry gensub("\r", "", "g", gensub("^[ ]", "", 1, $0)); 149 | # print "entry continuation: " entry 150 | } else if (insummary) { 151 | summary = summary gensub("\r", "", "g", gensub("^[ ]", "", 1, $0)) 152 | # print "summary continuation: " summary 153 | } else if (inattendee) { 154 | attendee = attendee gensub("\r", "", "g", gensub("^[ ]", "", 1, $0)) 155 | # print "attendee continuation: " attendee 156 | are_we_going(attendee) 157 | add_attendee(attendee) 158 | } else if (inlocation) { 159 | location = location unescape(gensub("\r", "", "g", $0), 0); 160 | 161 | } 162 | if (preserve) 163 | icalentry = icalentry "\n" $0 164 | } 165 | 166 | /^BEGIN:VEVENT/ { 167 | # start of an event: initialize global values used for each event 168 | date = ""; 169 | entry = "" 170 | headline = "" 171 | icalentry = "" # the full entry for inspection 172 | id = "" 173 | indescription = 0; 174 | insummary = 0 175 | inattendee = 0 176 | inlocation = 0 177 | in_alarm = 0 178 | got_end_date = 0 179 | attending = attending_types["UNSET"]; 180 | # http://unix.stackexchange.com/a/147958/129055 181 | intfreq = "" # the interval and frequency for repeating org timestamps 182 | lasttimestamp = -1; 183 | location = "" 184 | rrend = "" 185 | status = "" 186 | summary = "" 187 | attendee = "" 188 | delete people_attending; 189 | 190 | # if this is the first event, output the preamble from the iCal file 191 | if (first) { 192 | if(preamble) { 193 | print "* COMMENT original iCal preamble" 194 | print gensub("\r", "", "g", icalentry) 195 | } 196 | if (preserve) 197 | icalentry = "" 198 | first = 0; 199 | } 200 | } 201 | 202 | # any line that starts at the left with a non-space character is a new data field 203 | 204 | /^BEGIN:VALARM/ { 205 | # alarms have their own UID, DESCRIPTION, etc. We don't want these polluting the real fields 206 | in_alarm = 1 207 | } 208 | 209 | /^END:VALARM/ { 210 | in_alarm = 0 211 | } 212 | 213 | /^[A-Z]/ { 214 | # we do not copy DTSTAMP lines as they change every time you download 215 | # the iCal format file which leads to a change in the converted 216 | # org file as I output the original input. This change, which is 217 | # really content free, makes a revision control system update the 218 | # repository and confuses. 219 | if (preserve) 220 | if (! index("DTSTAMP", $1)) 221 | icalentry = icalentry "\n" $0 222 | # this line terminates the collection of description and summary entries 223 | indescription = 0; 224 | insummary = 0; 225 | inattendee = 0; 226 | } 227 | 228 | # this type of entry represents a day entry, not timed, with date stamp YYYYMMDD 229 | 230 | /^DTSTART;VALUE=DATE[^-]/ { 231 | date = datestring($2); 232 | } 233 | 234 | /^DTEND;VALUE=DATE[^-]/ { 235 | got_end_date = 1 236 | end_date = datestring($2, 1); 237 | if ( issameday ) 238 | end_date = "" 239 | } 240 | 241 | 242 | # this represents a timed entry with date and time stamp YYYYMMDDTHHMMSS 243 | # we ignore the seconds 244 | /^DTSTART[:;][^V]/ { 245 | tz = ""; 246 | match($0, /TZID=([^:]*)/, a) 247 | { 248 | tz = a[1]; 249 | } 250 | 251 | tz_offset = get_timezone_offset(tz) 252 | offset = local_tz_offset - tz_offset 253 | date = datetimestring($2, offset); 254 | # print date; 255 | 256 | if (date != "" && got_end_date) { 257 | fix_date_time() 258 | } 259 | } 260 | 261 | # and the same for the end date; 262 | 263 | /^DTEND[:;][^V]/ { 264 | # NOTE: this doesn't necessarily appear after DTSTART 265 | tz = ""; 266 | match($0, /TZID=([^:]*)/, a) 267 | { 268 | tz = a[1]; 269 | } 270 | tz_offset = get_timezone_offset(tz) 271 | offset = local_tz_offset - tz_offset 272 | 273 | end_date = datetimestring($2, offset); 274 | got_end_date = 1 275 | 276 | if (date != "" && got_end_date) { 277 | # We got start and end date/time, let's munge as appropriate 278 | fix_date_time() 279 | } 280 | } 281 | 282 | 283 | # this represents a timed entry with a UTC datetime stamp YYYYMMDDTHHMMSSZ 284 | # we ignore the seconds 285 | /^DTSTART[:;]VALUE=DATE-TIME/ { 286 | tz = ""; 287 | offset = local_tz_offset 288 | 289 | date = datetimestring($2, offset); 290 | # print date; 291 | 292 | if (date != "" && got_end_date) { 293 | fix_date_time() 294 | } 295 | } 296 | 297 | # and the same for the end date; 298 | 299 | /^DTEND[:;]VALUE=DATE-TIME/ { 300 | # NOTE: this doesn't necessarily appear after DTSTART 301 | tz = ""; 302 | offset = local_tz_offset 303 | 304 | end_date = datetimestring($2, offset); 305 | got_end_date = 1 306 | 307 | if (date != "" && got_end_date) { 308 | # We got start and end date/time, let's munge as appropriate 309 | fix_date_time() 310 | } 311 | } 312 | 313 | 314 | # repetition rule 315 | 316 | /^RRULE:FREQ=(DAILY|WEEKLY|MONTHLY|YEARLY)/ { 317 | # TODO: handle BYDAY values for events that repeat weekly for multiple days 318 | # (e.g. a "Gym" event) 319 | 320 | # get the d, w, m or y value 321 | freq = tolower(gensub(/.*FREQ=(.).*/, "\\1", 1, $0)) 322 | # get the interval, and use 1 if none specified 323 | interval = $2 ~ /INTERVAL=/ ? gensub(/.*INTERVAL=([0-9]+);.*/, "\\1", 1, $2) : 1 324 | # get the enddate of the rule and use "" if none specified 325 | rrend = $2 ~ /UNTIL=/ ? datestring(gensub(/.*UNTIL=([0-9]{8}).*/, "\\1", 1, $2)) : "" 326 | rrend_raw = $2 ~ /UNTIL=/ ? gensub(/.*UNTIL=([0-9]{8}).*/, "\\1", 1, $2) : "" 327 | repeat_count = $2 ~ /COUNT=/ ? gensub(/.*COUNT=([0-9]+).*/, "\\1", 1, $2) : "" 328 | # build the repetitor vale as understood by org 329 | intfreq = " +" interval freq 330 | # if the repetition is daily, and there is an end date, drop the repetitor 331 | # as that is the default 332 | if (intfreq == " +1d" && end_date == "" && rrend != "") 333 | intfreq = "" 334 | now = strftime("%Y%m%dT%H%M%SZ") 335 | if (rrend_raw != "" && rrend_raw < now) 336 | intfreq = "" 337 | if (repeat_count != "") # TODO: count repeats correctly 338 | intfreq = "" 339 | } 340 | 341 | # The description will the contents of the entry in org-mode. 342 | # this line may be continued. 343 | 344 | /^DESCRIPTION/ { 345 | if (!in_alarm) { 346 | # Setting $1 to "" clears colons from items like "1:1 with Marc", so we 347 | # strip "DESCRIPTION:" off of the front instead 348 | # $1 = ""; 349 | entry = entry gensub("\r", "", "g", gensub(/^DESCRIPTION:/, "", 1, $0)); 350 | indescription = 1; 351 | } 352 | } 353 | 354 | # the summary will be the org heading 355 | 356 | /^SUMMARY/ { 357 | # Setting $1 to "" clears colons from items like "1:1 with Marc", so we 358 | # strip "SUMMARY:" off of the front instead 359 | if (!in_alarm) { 360 | summary = gensub("\r", "", "g", gensub(/^SUMMARY:/, "", 1, $0)); 361 | 362 | # trim trailing dots if requested by config option 363 | if(trimdots && summary ~ /\.\.\.$/) 364 | sub(/\.\.\.$/, "", summary) 365 | insummary = 1; 366 | # print "Summary: " summary 367 | } 368 | } 369 | 370 | # the unique ID will be stored as a property of the entry 371 | 372 | /^UID/ { 373 | if (!in_alarm) { 374 | id = gensub("\r", "", "g", $2); 375 | } 376 | } 377 | 378 | /^LOCATION/ { 379 | location = unescape(gensub("\r", "", "g", $2), 0); 380 | inlocation = 1; 381 | # print "Location: " location 382 | } 383 | 384 | /^STATUS/ { 385 | status = gensub("\r", "", "g", $2); 386 | # print "Status: " status 387 | } 388 | 389 | /^ATTENDEE/ { 390 | attendee = gensub("\r", "", "g", $0); 391 | inattendee = 1; 392 | # print "Attendee: " attendee 393 | } 394 | 395 | # when we reach the end of the event line, we output everything we 396 | # have collected so far, creating a top level org headline with the 397 | # date/time stamp, unique ID property and the contents, if any 398 | 399 | /^END:VEVENT/ { 400 | #output event 401 | # print "max_age: " max_age 402 | # print "lasttimestamp: " lasttimestamp 403 | # print "lasttimestamp+max_age_seconds: " lasttimestamp+max_age_seconds 404 | # print "systime(): " systime() 405 | 406 | is_duplicate = (id in UIDS); 407 | if(is_duplicate == 0 && (max_age<0 || intfreq != "" || ( lasttimestamp>0 && systime()--<" end_date 416 | else if (rrend != "") 417 | date = date ">--<" rrend 418 | 419 | # translate \n sequences to actual newlines and unprotect commas (,) 420 | if (condense) 421 | print "* <" date "> " gensub("^[ ]+", "", "", unescape(summary, 0)) 422 | else 423 | print "* " gensub("^[ ]+", "", "g", unescape(summary, 0)) 424 | print " :PROPERTIES:" 425 | print " :ID: " id 426 | if(length(location)) 427 | print " :LOCATION: " location 428 | if(length(status)) 429 | print " :STATUS: " status 430 | attending_string = attending_types[attending] 431 | if(attending_string == "UNSET") 432 | # No attending info at all -- assume this is an event we 433 | # created to block off our calendar, with no attendees, and 434 | # mark it as attending 435 | attending_string = "ATTENDING" 436 | print " :ATTENDING: " attending_string 437 | print " :ATTENDEES: " join_keys(people_attending) 438 | print " :END:" 439 | if (date2 != "") 440 | { 441 | # Fake some logbook entries so we can generate a clock report 442 | print " :LOGBOOK:" 443 | print " CLOCK: [" date1 "]--[" date2 "] => " "0:00" 444 | print " :END" 445 | } 446 | if (!condense) 447 | print "<" date ">" 448 | 449 | print "" 450 | if(length(entry)>1) 451 | print gensub("^[ ]+", "", "g", unescape(entry, 1)); 452 | 453 | # output original entry if requested by 'original' config option 454 | if (original) 455 | print "** COMMENT original iCal entry\n", gensub("\r", "", "g", icalentry) 456 | } 457 | UIDS[id] = 1; 458 | } 459 | } 460 | 461 | 462 | # Join keys in an array, return a string 463 | function join_keys(input) 464 | { 465 | joined = ""; 466 | first_key = 1; 467 | for (key in input) 468 | { 469 | if (first_key != 1) 470 | joined = joined ", " 471 | joined = joined key 472 | first_key = 0; 473 | } 474 | return joined; 475 | } 476 | 477 | 478 | 479 | # unescape commas, newlines, etc. newlines are optionally converted to just 480 | # spaces -- it's good to preserve them in descriptions for e.g. interview 481 | # calendar events, but addresses look better with spaces as more info fits on a 482 | # line 483 | function unescape(input, preserve_newlines) 484 | { 485 | ret = gensub("\\\\,", ",", "g", 486 | gensub("\\\\;", ";", "g", input)) 487 | if (preserve_newlines) 488 | ret = gensub("\\\\n", "\n", "g", ret) 489 | else 490 | ret = gensub("\\\\n", " ", "g", ret) 491 | return ret 492 | # return gensub("\\\\,", ",", "g", 493 | # gensub("\\\\n", " ", "g", 494 | # gensub("\\\\;", ";", "g", input))) 495 | } 496 | 497 | # function to parse a timezone offset to minutes minutes 498 | function parse_timezone_offset(offset_string) { 499 | hours = substr(offset_string, 2, 2) * 60; 500 | minutes = substr(offset_string, 4, 2); 501 | total_offset = hours + minutes; 502 | if (substr(offset_string, 1, 1) == "-") { 503 | total_offset = -total_offset; 504 | } 505 | return total_offset; 506 | } 507 | 508 | # Get timezone offset for a given timezone 509 | function get_timezone_offset(tz) { 510 | # Construct a command to get the timezone offset for 'tz' 511 | cmd = "TZ=\"" tz "\" date +%z" 512 | 513 | # Run the command and read the output 514 | cmd | getline tz_offset 515 | close(cmd) 516 | 517 | return parse_timezone_offset(tz_offset) 518 | } 519 | 520 | # funtion to convert an iCal time string 'yyyymmddThhmmss[Z]' into a 521 | # date time string as used by org, preferably including the short day 522 | # of week: 'yyyy-mm-dd day hh:mm' or 'yyyy-mm-dd hh:mm' if we cannot 523 | # define the day of the week 524 | 525 | function datetimestring(input, offset) 526 | { 527 | # print "________" 528 | # print "input : " input 529 | # convert the iCal Date+Time entry to a format that mktime can understand 530 | spec = match(input, "([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2}).*[\r]*", a); 531 | year = a[1] 532 | month = a[2] 533 | day = a[3] 534 | hour = a[4] 535 | min = a[5] 536 | sec = a[6] 537 | # print "spec :" spec 538 | 539 | # print "input: " input 540 | # print "datetime: " year" "month" "day" "hour" "min" "sec 541 | stamp = mktime(year" "month" "day" "hour" "min" "sec); 542 | lasttimestamp = stamp; 543 | 544 | if (stamp <= 0) { 545 | # this is a date before the start of the epoch, so we cannot 546 | # use strftime and will deliver a 'yyyy-mm-dd hh:mm' string 547 | # without day of week; this assumes local time, and does not 548 | # attempt UTC offset correction 549 | spec = gensub("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2}).*[\r]*", "\\1-\\2-\\3 \\4:\\5", "g", input); 550 | # print "==> spec:" spec; 551 | return spec; 552 | } 553 | 554 | stamp = stamp + offset * 60; 555 | 556 | return strftime("%Y-%m-%d %a %H:%M", stamp); 557 | } 558 | 559 | # function to convert an iCal date into an org date; 560 | # the optional parameter indicates whether this is an end date; 561 | # for single or multiple whole day events, the end date given by 562 | # iCal is the date of the first day after the event; 563 | # if the optional 'isenddate' parameter is non zero, this function 564 | # tries to reduce the given date by one day 565 | 566 | function datestring(input, isenddate) 567 | { 568 | #convert the iCal string to a an mktime input string 569 | spec = gensub("([0-9]{4})([0-9]{2})([0-9]{2}).*[\r]*", "\\1 \\2 \\3 00 00 00", "g", input); 570 | 571 | # compute the nr of seconds after or before the epoch 572 | # dates before the epoch will have a negative timestamp 573 | # days after the epoch will have a positive timestamp 574 | stamp = mktime(spec); 575 | 576 | if (isenddate) { 577 | # subtract 1 day from the timestamp 578 | # note that this also works for dates before the epoch 579 | stamp = stamp - 86400; 580 | 581 | # register whether the end date is same as the start date 582 | issameday = lasttimestamp == stamp 583 | } 584 | # save timestamp to allow for check of max_age 585 | lasttimestamp = stamp 586 | 587 | if (stamp < 0) { 588 | # this date is before the epoch; 589 | # the returned datestring will not have the short day of week string 590 | # as strftime does not handle negative times; 591 | # we have to construct the datestring directly from the input 592 | if (isenddate) { 593 | # we really should return the date before the input date, but strftime 594 | # does not work with negative timestamp values; so we can not use it 595 | # to obtain the string representation of the corrected timestamp; 596 | # we have to return the date specified in the iCal input and we 597 | # add time 00:00 to clarify this 598 | return spec = gensub("([0-9]{4})([0-9]{2})([0-9]{2}).*[\r]*", "\\1-\\2-\\3 00:00", "g", input); 599 | } else { 600 | # just generate the desired representation of the input date, without time; 601 | return gensub("([0-9]{4})([0-9]{2})([0-9]{2}).*[\r]*", "\\1-\\2-\\3", "g", input); 602 | } 603 | } 604 | 605 | # return the date and day of week 606 | return strftime("%Y-%m-%d %a", stamp); 607 | } 608 | 609 | # Add the current attendee's response to a set, so we can list who's going 610 | # and who's declined 611 | function add_attendee(attendee) 612 | { 613 | match(attendee, /CN=([^;]+)/, m) 614 | { 615 | CN = tolower(m[1]); 616 | people_attending[CN] = 1; 617 | } 618 | } 619 | 620 | function fix_date_time() 621 | { 622 | if (substr(date,1,10) == substr(end_date,1,10)) { 623 | # timespan within same date, use one date with a time range, but preserve 624 | # original dates for org-clocktable 625 | date1 = date 626 | date2 = end_date 627 | 628 | date = date "-" substr(end_date, length(end_date)-4) 629 | end_date = "" 630 | } 631 | } 632 | 633 | # Parse the current ATTENDEE line and see if it belongs to us. If so, check if 634 | # we've accepted this calendar invite, and if so, set `attending` to True. It 635 | # may be the case that there are no attendees (e.g. personal calendar items), 636 | # and if that's the case, we'll leave `attending` unset. If there are attendees, 637 | # we'll parse our status out and set `attending` appropriately. 638 | function are_we_going(attendee) 639 | { 640 | if (attending != attending_types["UNSET"]) 641 | { 642 | # print "Bailing out early, attending is " attending 643 | return; 644 | } 645 | 646 | match(attendee, /CN=([^;]+)/, m) 647 | { 648 | # CN's can optionally be surrounded by quotes (google calendar download 649 | # omits, apple calendar export includes them) 650 | CN = gensub("\"", "", "g", tolower(m[1])); 651 | # TODO: no hardcoding 652 | if (CN == tolower(author) || CN == tolower(emailaddress)) 653 | { 654 | # This is us -- did we accept the meeting? 655 | if (attendee ~ /PARTSTAT=ACCEPTED/) 656 | { 657 | attending = attending_types["ATTENDING"]; 658 | } 659 | else if (attendee ~ /PARTSTAT=NEEDS-ACTION/) 660 | { 661 | attending = attending_types["NEEDS_ACTION"]; 662 | } 663 | else { 664 | attending = attending_types["NOT_ATTENDING"]; 665 | } 666 | } 667 | } 668 | # print "are_we_going: " attending 669 | } 670 | 671 | # Local Variables: 672 | # time-stamp-line-limit: 1000 673 | # time-stamp-format: "%04y.%02m.%02d %02H:%02M:%02S" 674 | # time-stamp-active: t 675 | # time-stamp-start: "Last change:[ \t]+" 676 | # time-stamp-end: "$" 677 | # End: 678 | --------------------------------------------------------------------------------