vcal2xapia.awk 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #/usr/bin/awk -f
  2. # Edmond Orignac (c) 2016. This awk program
  3. # attempts to convert .ics/.vcs files into XAPIA format for dtcm(1) appointment.
  4. # It is redistributable under the terms of MIT License.
  5. BEGIN {FS=":"}
  6. /^BEGIN/ {if ($2~"VEVENT") {appnt=1; rxtype=0; nxr=0; mxday=0; mxmonth=0; runtil=0; xinterval=0} else if ($2~"VTODO") {appnt=2 ; rxtype=0; nxr=0; mxday=0; mxmonth=0; runtil=0;xinterval=0}}
  7. /^TZ/ {if ($2~"[+-][1-9]*") {timezone=$2} else {timezone=1}}
  8. # Knowing the timezone, we can convert local time to UTC time.
  9. # Unfortunately, it is only working if the timezone is indicated by
  10. # a number as in "TZ:+03" not in the case of TZ=Europe/Paris.
  11. # If we fail to get a numeric value, we assume the timezone is UTC+1
  12. /^DTSTART/ {sdate=$2}
  13. /^DTEND/ {fdate=$2}
  14. /^DUE/ {ddate=$2; tsksts=2304}
  15. /^COMPLETED/ {ddate=$2;tsksts=6}
  16. /^DESCRIPTION/ {summary=summary" "substr($0,13)}
  17. /^SUMMARY/ {summary=summary" "substr($0,9)}
  18. /^LOCATION/ {summary=summary" in "substr($0,10)}
  19. /^RRULE/ {rrule=$2;
  20. # The event will repeat forever unless we find a limit
  21. nxr=0;
  22. rfields=split(rrule,rdata,";");
  23. for (i=1;i<=rfields;i++) {
  24. if (rdata[i]~"FREQ") {
  25. rtype=substr(rdata[i],5);
  26. if (rtype~"DAILY") rxtype=1;
  27. if (rtype~"WEEKLY") rxtype=2;
  28. # We assume a monthly repeat by date for now
  29. if (rtype~"MONTHLY") rxtype=5;
  30. if (rtype~"YEARLY") rxtype=6;
  31. }
  32. if (rdata[i]~"COUNT") {nxr=substr(rdata[i],7)};
  33. if (rdata[i]~"BYDAY") {
  34. rdays=substr(rdata[i],7);
  35. nrdays=split(rdata[i],ddays,",");
  36. # nrdays=1: we have a simple monthly repeat by weekday
  37. if ((nrdays==1) && (rxtype==5)) {rxtype=4};
  38. # with nrdays > 1 we have a problem:
  39. # XAPIA format only allows limited forms of weekly repeats
  40. if ((nrdays>1) && (rxtype=2)) {
  41. if (nrdays==2) {
  42. tuth=match(ddays[1],"TU")+match(ddays[2],"TH");
  43. if (tuth==2) {rxtype=12};
  44. };
  45. if (nrdays==3) {
  46. mowefr=0;
  47. for (j=1;j<=nrdays;j++) {
  48. mowefr+=match(ddays[j],"MO");
  49. mowefr+=match(ddays[j],"WE");
  50. mowefr+=match(ddays[j],"FR");
  51. };
  52. if (mowefr==3) {rxtype=11};
  53. };
  54. if (nrdays==5) {
  55. wweek=0;
  56. for (j=1;j<=nrdays;j++) {
  57. wweek+=match(ddays[j],"MO");
  58. wweek+=match(ddays[j],"TU");
  59. wweek+=match(ddays[j],"WE");
  60. wweek+=match(ddays[j],"TH");
  61. wweek+=match(ddays[j],"FR");
  62. };
  63. if (wweek==5) {rxtype=10};
  64. };
  65. };
  66. };
  67. # This is bad: if we have not found a repeat by day matching one XAPIA
  68. # format, the repetition rule is not fully defined.
  69. # Most likely we will have only one of the appointments of the week.
  70. if (rdata[i]~"BYMONTHDAY=") {mxday=substr(rdata[i],12)};
  71. if (rdata[i]~"BYMONTH=") {mxmonth=substr(rdata[i],9)};
  72. if (rdata[i]~"INTERVAL=") {xinterval=substr(rdata[i],10)
  73. # Weekly appointment with a 2 week interval is definec in XAPIA
  74. if ((xinterval==2) && (rxtype==2)) {rxtype=3}
  75. # Monthly appointment with a 12 month interval is really a yearly appointment
  76. if ((xinterval==12) && (rxtype=5)) {rxtype=6}
  77. # Weekly appointment with N>2 interval
  78. if ((xinterval>2) && (rxtype==2)) {rxtype=8}
  79. # Monthly appointment with N>=2 interval (12 month excluded)
  80. if ((xinterval>=2) && (rxtype==5)) {rxtype=9}
  81. # Daily appointment with N>=2 interval
  82. if ((xinterval>=2) && (rxtype==1)) {rxtype=7}
  83. };
  84. # Repetition until a date in the future
  85. if (rdata[i]~"UNTIL=") {xuntil=substr(rdata[i],7); runtil=1};
  86. };
  87. };
  88. /^END/ {if ($2~"VEVENT") {
  89. # # That one is annoying: we have to calculate the number of repetitions !
  90. # We do that by using an average duration for the year and for the month
  91. # but obviously this is imprecise, and the last appointment may not be there.
  92. # Also, we must do it here since DTSTART can appear at the very end
  93. # of an entry. The arithmetics gets sillier with repeat every n week/day/month...
  94. if (runtil==1) {
  95. uyear=substr(xuntil,1,4)-substr(sdate,1,4);
  96. umonth=substr(xuntil,5,2)-substr(sdate,5,2);
  97. uday=substr(xuntil,7,2)-substr(sdate,7,2);
  98. if (rxtype==1) nxr=int(365.25*uyear+30.44*umonth+uday)+1;
  99. if (rxtype==2) nxr=int((365.25*uyear+30.44*umonth+uday)/7.0)+1;
  100. if (rxtype==3) nxr=int((365.25*uyear+30.44*umonth+uday)/14.0)+1;
  101. if ((rxtype==4)||(rxtype==5)) nxr=12*uyear+umonth+1;
  102. if (rxtype==6) nxr=uyear+1;
  103. if (rxtype==7) nxr=int((365.25*uyear+30.44*umonth+uday)/xinterval)+1;
  104. if (rxtype==8) nxr=int((365.25*uyear+30.44*umonth+uday)/(7*xinterval))+1;
  105. if (rxtype==9) nxr=int((12*uyear+umonth)/xinterval)+1;
  106. if (rxtype==10) nxr=int(5.0*(365.25*uyear+30.44*umonth+uday)/7.0)+1;
  107. if (rxtype==11) nxr=int(3.0*(365.25*uyear+30.44*umounth+uday)/7.0)+1;
  108. if (rxtype==12) nxr=int(3.0*(365.25*uyear+30.44*umounth+uday)/7.0)+1;
  109. if (nxr<0) nxr=0;
  110. };
  111. # Another important thing is that dtcm does not like events that spread
  112. # over more than 24 hours. So we have to find how many days is fdate-sdate
  113. # and if that is more than 1 day transform the event into a daily repeat.
  114. dyear=substr(fdate,1,4)-substr(sdate,1,4);
  115. dmonth=substr(fdate,5,2)-substr(sdate,5,2);
  116. dday=int(substr(fdate,7,2)-substr(sdate,7,2)+30.44*dmonth+365.25*dyear);
  117. if ((rxtype==0) && (dday>0)) {rxtype=1;nxr=dday+1};
  118. # Start hour and End hour have to be converted to UTC first if timezone is defined.
  119. printf("\n\n")
  120. print "\t** Calendar Appointment **"
  121. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Entry Delimiter//EN:string:begin";
  122. printf("-//XAPIA/CSA/ENTRYATTR//NONSGML Start Date//EN:datetime:%s\n",sdate);
  123. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Type//EN:uinteger:0";
  124. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Subtype//EN:string:-//XAPIA/CSA/SUBTYPE//NONSGML Subtype Appointment//EN";
  125. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Classification//EN:uinteger:0";
  126. if (rxtype==0) {
  127. printf("-//XAPIA/CSA/ENTRYATTR//NONSGML End Date//EN:datetime:%s\n",fdate);
  128. } else {
  129. qdate=substr(sdate,1,8) substr(fdate,9);
  130. printf("-//XAPIA/CSA/ENTRYATTR//NONSGML End Date//EN:datetime:%s\n",qdate);
  131. };
  132. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Show Time//EN:sinteger:1";
  133. printf("-//XAPIA/CSA/ENTRYATTR//NONSGML Summary//EN:string:%s\n",summary);
  134. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Status//EN:uinteger:2304";
  135. printf("-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Repeat Type//EN:sinteger:%d\n",rxtype);
  136. printf("-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Repeat Times//EN:uinteger:%d\n",nxr);
  137. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Audio Reminder//EN:reminder:300:";
  138. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Popup Reminder//EN:reminder:300:";
  139. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Repeat Occurrence Number//EN:sinteger:-1";
  140. printf("-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Repeat Interval//EN:uinteger:%d\n",rinterval);
  141. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Entry Delimiter//EN:string:end";
  142. printf("\tDate: %s/%s/%s\n",substr(sdate,5,2),substr(sdate,7,2),substr(sdate,1,4));
  143. # The start/end time are in UTC and have to be converted to local time. We assume the local time is UTC+1 unless we know the shift from UTC.
  144. shour=substr(sdate,10,2);
  145. smin=substr(sdate,12,2);
  146. fhour=substr(fdate,10,2);
  147. fmin=substr(fdate,12,2);
  148. if ((fhour+fmin+shour+smin)==0) {fhour=01;fmin=42;shour=01;smin=41}
  149. shour+=timezone;
  150. fhour+=timezone;
  151. printf("\tStart: %.2d%.2d\n",shour,smin)
  152. printf("\tEnd: %.2d%.2d\n",fhour,fmin)
  153. if (rxtype==0) {print "\tRepeat: One Time"};
  154. if (rxtype==1) {print "\tRepeat: Daily"};
  155. if (rxtype==2) {print "\tRepeat: Weekly"};
  156. if (rxtype==3) {print "\tRepeat: Every Two Weeks"};
  157. if (rxtype==4) {print "\tRepeat: Monthly By Weekday"};
  158. if (rxtype==5) {print "\tRepeat: Monthly By Date"};
  159. if (rxtype==6) {print "\tRepeat: Yearly"}
  160. if (rxtype==7) {printf("\t Repeat Every %d days\n",xinterval)}
  161. if (rxtype==8) {printf("\t Repeat Every %d weeks\n",xinterval)}
  162. if (rxtype==9) {printf("\t Repeat Every %d months\n",xinterval)}
  163. if (rxtype==10) {print "\tRepeat: Monday thru Friday"};
  164. if (rxtype==11) {print "\tRepeat: Mon, Wed, Fri"};
  165. if (rxtype==12) {print "\tRepeat: Tuesday, Thursday"};
  166. printf("\tFor: %d\n",nxr);
  167. printf("\tWhat: %s\n",summary);
  168. printf("\t\n");
  169. sdate="";
  170. fdate="";
  171. appnt=0;
  172. summary="";
  173. } else if ($2~"VTODO") {
  174. # We are reproducing the code for repetition of an event above. This could be refactored as a function to make the program more elegant.
  175. if (runtil==1) {
  176. uyear=substr(xuntil,1,4)-substr(sdate,1,4);
  177. umonth=substr(xuntil,5,2)-substr(sdate,5,2);
  178. uday=substr(xuntil,7,2)-substr(sdate,7,2);
  179. if (rxtype==1) nxr=int(365.25*uyear+30.44*umonth+uday)+1;
  180. if (rxtype==2) nxr=int((365.25*uyear+30.44*umonth+uday)/7.0)+1;
  181. if (rxtype==3) nxr=int((365.25*uyear+30.44*umonth+uday)/14.0)+1;
  182. if ((rxtype==4)||(rxtype==5)) nxr=12*uyear+umonth+1;
  183. if (rxtype==6) nxr=uyear+1;
  184. if (rxtype==7) nxr=int((365.25*uyear+30.44*umonth+uday)/xinterval)+1;
  185. if (rxtype==8) nxr=int((365.25*uyear+30.44*umonth+uday)/(7*xinterval))+1;
  186. if (rxtype==9) nxr=int((12*uyear+umonth)/xinterval)+1;
  187. if (rxtype==10) nxr=int(5.0*(365.25*uyear+30.44*umonth+uday)/7.0)+1;
  188. if (rxtype==11) nxr=int(3.0*(365.25*uyear+30.44*umounth+uday)/7.0)+1;
  189. if (rxtype==12) nxr=int(3.0*(365.25*uyear+30.44*umounth+uday)/7.0)+1;
  190. if (nxr<0) nxr=0;
  191. };
  192. # Start hour and End hour have to be converted to UTC first if timezone is defined.
  193. printf("\n\n")
  194. print "\t** Calendar Appointment **"
  195. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Entry Delimiter//EN:string:begin";
  196. printf("-//XAPIA/CSA/ENTRYATTR//NONSGML Start Date//EN:datetime:%s\n",ddate);
  197. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Type//EN:uinteger:1";
  198. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Classification//EN:uinteger:0";
  199. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Show Time//EN:sinteger:1"
  200. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Show Time//EN:sinteger:1";
  201. printf("-//XAPIA/CSA/ENTRYATTR//NONSGML Summary//EN:string:%s\n",summary);
  202. printf ("-//XAPIA/CSA/ENTRYATTR//NONSGML Status//EN:uinteger:%d\n",tsksts);
  203. printf("-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Repeat Type//EN:sinteger:%d\n",rxtype);
  204. printf("-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Repeat Times//EN:uinteger:%d\n",nxr);
  205. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Audio Reminder//EN:reminder:300:";
  206. print "-//XAPIA/CSA/ENTRYATTR//NONSGML Popup Reminder//EN:reminder:300:";
  207. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Repeat Occurrence Number//EN:sinteger:-1";
  208. printf("-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Repeat Interval//EN:uinteger:%d\n",rinterval);
  209. print "-//CDE_XAPIA_PRIVATE/CSA/ENTRYATTR//NONSGML Entry Delimiter//EN:string:end";
  210. printf("\tDate: %s/%s/%s\n",substr(ddate,5,2),substr(ddate,7,2),substr(ddate,1,4));
  211. # The start/end time are in UTC and have to be converted to local time. We assume the local time is UTC+1 unless we have a numeric value for timezone shift.
  212. shour=substr(ddate,10,2);
  213. smin=substr(ddate,12,2);
  214. if ((shour+smin)==0) {shour=01;smin=41}
  215. shour+=timezone;
  216. printf("\tStart: %.2d%.2d\n",shour,smin)
  217. if (rxtype==0) {print "\tRepeat: One Time"};
  218. if (rxtype==1) {print "\tRepeat: Daily"};
  219. if (rxtype==2) {print "\tRepeat: Weekly"};
  220. if (rxtype==3) {print "\tRepeat: Every Two Weeks"};
  221. if (rxtype==4) {print "\tRepeat: Monthly By Weekday"};
  222. if (rxtype==5) {print "\tRepeat: Monthly By Date"};
  223. if (rxtype==6) {print "\tRepeat: Yearly"}
  224. if (rxtype==7) {printf("\t Repeat Every %d days\n",xinterval)}
  225. if (rxtype==8) {printf("\t Repeat Every %d weeks\n",xinterval)}
  226. if (rxtype==9) {printf("\t Repeat Every %d months\n",xinterval)}
  227. if (rxtype==10) {print "\tRepeat: Monday thru Friday"};
  228. if (rxtype==11) {print "\tRepeat: Mon, Wed, Fri"};
  229. if (rxtype==12) {print "\tRepeat: Tuesday, Thursday"};
  230. printf("\tFor: %d\n",nxr);
  231. printf("\tWhat: %s\n",summary);
  232. printf("\t\n");
  233. sdate="";
  234. fdate="";
  235. appnt=0;
  236. summary="";
  237. };
  238. }