2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /*******************************************************************************
27 ** $TOG: cm_tty.c /main/9 1998/04/17 11:22:38 mgreess $
29 ** RESTRICTED CONFIDENTIAL INFORMATION:
31 ** The information in this document is subject to special
32 ** restrictions in a confidential disclosure agreement between
33 ** HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
34 ** document outside HP, IBM, Sun, USL, SCO, or Univel without
35 ** Sun's specific written approval. This document and all copies
36 ** and derivative works thereof must be returned or destroyed at
39 ** Copyright 1993 Sun Microsystems, Inc. All rights reserved.
41 *******************************************************************************/
44 * (c) Copyright 1993, 1994 Hewlett-Packard Company *
45 * (c) Copyright 1993, 1994 International Business Machines Corp. *
46 * (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
47 * (c) Copyright 1993, 1994 Novell, Inc. *
51 static char sccsid[] = "@(#)cm_tty.c 1.91 95/07/27 Copyr 1993 Sun Microsystems, Inc.";
54 #include <EUSCompat.h>
55 #include <sys/types.h>
61 #include <sys/param.h>
62 #include <sys/types.h>
67 /*******************************************************************************
71 *******************************************************************************/
72 static char *separator_strs[] = {
79 static char *repeat_strs[11];
81 static char *default_repeat_cnt_strs[] = {
95 static char *default_repeat_scope_strs[11];
97 static char *for_strs[] = {
114 static char *time_scope_strs_i18n[3];
115 static char *time_scope_strs[] = {
121 static char *repeat_scope_strs[3];
123 static char *day_strs[] = {
134 static char *month_strs[] = {
157 static char *new_appt_begin_delimiter = NULL;
158 static char *new_appt_end_delimiter = NULL;
160 extern int _csa_iso8601_to_tick(char *, time_t*);
161 extern int _csa_tick_to_iso8601(time_t, char *);
162 extern int _csa_iso8601_to_duration(char *, int*);
163 extern int _csa_duration_to_iso8601(int, char *);
165 /*******************************************************************************
169 *******************************************************************************/
171 copy_and_pad_newlines(char *dest, char *source) {
173 if ((*dest++ = *source++) == '\n')
178 count_newlines(char *string) {
184 if (*string++ == '\n')
198 if (type == TTY_Insert)
199 fprintf(stderr, "%s",
200 catgets(catd, 1, 1042, "Insert Access Denied: "));
201 else if (type == TTY_Delete)
202 fprintf(stderr, "%s",
203 catgets(catd, 1, 1043, "Delete Access Denied: "));
205 fprintf(stderr, "%s",
206 catgets(catd, 1, 1044, "Lookup Access Denied: "));
208 if (appt_what && appt_what[0] != '\0') {
209 buf = cm_strdup(appt_what);
210 if (ptr = strrchr(buf, '\n'))
212 fprintf(stderr, "%s '%s'\n",
213 catgets(catd, 1, 1045, "Cancelled for"),
217 fprintf(stderr, "%s\n",
218 catgets(catd, 1, 1046,
219 "Appointment Cancelled\n"));
223 query_user(void *client_data) {
224 char ans[MAXNAMELEN], *what_str = (char *)client_data;
228 The following four messages (1047-1050) will be printed to stdout
229 and can have the following two forms:
231 "This appointment: '<appt text>' has an end
232 time earlier than its begin time. Do you
233 want to schedule it into the next day? [Y/N] "
237 "This appointment has an end
238 time earlier than its begin time. Do you
239 want to schedule it into the next day? [Y/N] "
241 The text <appt text> and [Y/N] should not be translated.
245 if (what_str && what_str[0] != '\0')
246 fprintf(stdout, catgets(catd_global, 1, 1047,
247 "This appointment: '%s' has an end\n"), what_str);
249 fprintf(stdout, "%s", catgets(catd_global, 1, 1048,
250 "This appointment has an end\n"));
251 fprintf(stdout, "%s", catgets(catd_global, 1, 1049,
252 "time earlier than its begin time. Do you\n"));
253 fprintf(stdout, "%s", catgets(catd_global, 1, 1050,
254 "want to schedule it into the next day? [Y/N] "));
255 fgets(ans, sizeof(ans)-1, stdin);
256 fprintf(stdout, "\n");
257 if (*ans == 'y' || *ans == 'Y')
262 /*******************************************************************************
264 ** External functions
266 *******************************************************************************/
268 boolean_str(boolean_t val) {
269 return (val ? "True" : "False");
273 * Delete an appointment. index is the nth appointment in the passed array.
278 CSA_session_handle session,
281 CSA_entry_handle *list) {
284 Dtcm_appointment *appt;
286 if (index < 0 || !list[index])
289 appt = allocate_appt_struct(appt_read,
291 CSA_ENTRY_ATTR_SUMMARY_I,
292 CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I,
295 if (query_appt_struct(session, list[index], appt) != CSA_SUCCESS) {
296 mini_err_msg(catd, appt->what->value->item.string_value,
301 if (appt->repeat_type->value->item.sint32_value == CSA_X_DT_REPEAT_ONETIME)
306 Message numbers 1051-1057 are printed to stdout and
309 "The appointment '<appt text>' is part of a repeating series. Do you want to:
310 1. Delete all of them
311 2. Delete this on only
317 fprintf(stdout, catgets(catd, 1, 1051,
318 "The appointment '%s' is part of a repeating series. "),
319 appt->what->value->item.string_value);
320 fprintf(stdout, "%s", catgets(catd, 1, 1052, "Do you want to:"));
321 fprintf(stdout, "%s", catgets(catd, 1, 1053,
322 "\n\t1. Delete all of them"));
323 fprintf(stdout, "%s", catgets(catd, 1, 1054,
324 "\n\t2. Delete this one only"));
325 fprintf(stdout, "%s", catgets(catd, 1, 1055, "\n\t3. Delete forward"));
326 fprintf(stdout, "%s", catgets(catd, 1, 1056, "\n\t4. Cancel"));
327 fprintf(stdout, "%s", catgets(catd, 1, 1057, "\n\tOption [1-4]: "));
328 fgets(ans, sizeof(ans)-1, stdin);
329 fprintf(stdout, "\n");
334 if (csa_delete_entry(session, list[index], CSA_SCOPE_ALL, NULL)
336 mini_err_msg(catd, appt->what->value->item.string_value,
340 if (csa_delete_entry(session, list[index], CSA_SCOPE_ONE, NULL)
342 mini_err_msg(catd, appt->what->value->item.string_value,
346 if (csa_delete_entry(session, list[index], CSA_SCOPE_FORWARD,
347 NULL) != CSA_SUCCESS)
348 mini_err_msg(catd, appt->what->value->item.string_value,
355 free_appt_struct(&appt);
361 * Build ascii date/time line from integer (tick)
364 cm_tty_format_header(Props *p, Tick tick, char *buf) {
371 d_op = (Days_op)dow(tick);
372 m_op = (Months_op)(month(tick) - 1);
374 switch(get_int_prop(p, CP_DATEORDERING)) {
376 sprintf(buf, "%s %s %d, %d", day_str(d_op), month_str(m_op),
377 dom(tick), year(tick));
380 sprintf(buf, "%s %d %s, %d", day_str(d_op), dom(tick),
381 month_str(m_op), year(tick));
384 sprintf(buf, "%s, %d %s %d", day_str(d_op), year(tick),
385 month_str(m_op), dom(tick));
394 scrub_attr_list(Dtcm_appointment *appt) {
398 for (i = 0; i < appt->count; i++) {
399 if (appt->attrs[i].value->type == CSA_VALUE_REMINDER) {
400 if ((appt->attrs[i].value->item.reminder_value->lead_time == NULL) ||
401 (appt->attrs[i].value->item.reminder_value->lead_time[0] == '\0')) {
402 free(appt->attrs[i].name);
403 appt->attrs[i].name = NULL;
406 else if ((appt->attrs[i].value->type == CSA_VALUE_ACCESS_LIST) && (appt->attrs[i].value->item.access_list_value == NULL)) {
407 free(appt->attrs[i].name);
408 appt->attrs[i].name = NULL;
410 else if ((appt->attrs[i].value->type == CSA_VALUE_STRING) && (appt->attrs[i].value->item.string_value == NULL)) {
411 free(appt->attrs[i].name);
412 appt->attrs[i].name = NULL;
414 else if ((appt->attrs[i].value->type == CSA_VALUE_DATE_TIME) && (appt->attrs[i].value->item.date_time_value == NULL)) {
415 free(appt->attrs[i].name);
416 appt->attrs[i].name = NULL;
422 * Insert an appointment!
425 cm_tty_insert(nl_catd catd, CSA_session_handle target, int version,
426 char *date, char *start, char *end,
427 char *repeat, char *repeatfor, char *what, char *filename,
429 int ret_stat = 0, cnt;
431 CSA_entry_handle new_entry;
432 CmDataList *list = CmDataListCreate();
434 CSA_attribute *attrs;
435 CSA_return_code status;
436 Dtcm_appointment *appt;
438 /* XXX: This is ugly but the query_user() function needs the catd
439 * and this is the easiest way to get it there.
444 op = parse_appt_from_file(catd, filename, list, p, query_user,
446 appt = (Dtcm_appointment *)CmDataListGetData(list, 1);
448 appt = allocate_appt_struct(appt_write, version, NULL);
449 load_appt_defaults(appt, p);
451 op = validate_appt(catd, appt, start, end, date, 0, what,
452 repeat, repeatfor, query_user, what, version);
454 CmDataListAdd(list, (void *)appt, 0);
456 for (cnt = 1; cnt <= list->count; cnt++) {
457 if ((appt = (Dtcm_appointment *)CmDataListGetData(list, cnt)) == NULL)
462 t1 = catgets(catd, 1, 1058, "Invalid Date specified.\n");
465 t1 = catgets(catd, 1, 1059,
466 "Invalid Start time specified.\n");
469 t1 = "Invalid Due time specified.\n";
472 t1 = catgets(catd, 1, 1060,
473 "Invalid Stop time specified.\n");
476 t1 = catgets(catd, 1, 1061,
477 "Empty or missing Date field.\n");
480 t1 = catgets(catd, 1, 1062,
481 "Empty or missing Start field.\n");
484 t1 = "Empty or missing Due time field.\n";
487 t1 = catgets(catd, 1, 1063,
488 "Empty or missing What field.\n");
490 case REPEAT_FOR_MISMATCH:
491 t1 = catgets(catd, 1, 1064,
492 "Repeat and For field mismatch.\n");
500 t1 = catgets(catd, 1, 1065,
501 "Insert appointment was cancelled\n");
505 if (op == VALID_APPT) {
506 scrub_attr_list(appt);
508 if ((status = csa_add_entry(target, appt->count, appt->attrs, &new_entry, NULL)) != CSA_SUCCESS) {
510 appt->what->value->item.string_value,
514 csa_free((CSA_buffer)new_entry);
516 char *msg = strdup(t1);
517 fprintf(stderr, "%s%s\n", msg, catgets(catd, 1, 1066,
518 "Appointment was not inserted."));
524 if ((list->count == 0) && (op != VALID_APPT || op != CANCEL_APPT))
527 for (cnt = 1; cnt <= list->count; cnt++)
528 if (appt = (Dtcm_appointment *)CmDataListGetData(list, cnt))
529 free_appt_struct(&appt);
530 CmDataListDestroy(list, B_FALSE);
536 cm_tty_load_props(Props **p) {
542 *p = (Props *)ckalloc(sizeof(Props));
544 cal_convert_cmrc(*p);
546 if ((start = get_int_prop(*p, CP_DAYBEGIN)) < 0)
550 if ((stop = get_int_prop(*p, CP_DAYEND)) <= start)
554 set_int_prop(*p, CP_DAYBEGIN, start);
555 set_int_prop(*p, CP_DAYEND, stop);
559 cm_tty_lookup(nl_catd catd, CSA_session_handle target, int version, char *date, char *view, CSA_entry_handle **list,
561 int span, day, lineno = 1, last_day = -1, i;
563 char start_buf[MAXNAMELEN], end_buf[MAXNAMELEN];
564 char buf[MAXNAMELEN], date_str[MAXNAMELEN], *what;
565 time_t tick, start, stop;
566 Lines *lines = NULL, *next_line;
569 CSA_attribute *range_attrs;
570 Dtcm_appointment *appt;
571 Tick start_tick, end_tick = 0;
572 CSA_return_code status;
575 * Preliminary stuff - set defaults
578 switch(get_int_prop(p, CP_DEFAULTVIEW)) {
592 else if ((tick = cm_getdate(date, NULL)) < 0) {
593 fprintf(stdout, "\n%s %s\n\n",
594 catgets(catd, 1, 1067, "Invalid date specified:"),
600 * Compute day and span for view specified
602 if (strncasecmp(view, "week", 4) == 0) {
605 } else if (strncasecmp(view, "month", 5) == 0) {
607 span = monthlength(tick);
613 start = lowerbound(tick - (day * daysec));
614 stop = next_ndays(start, span) - 1;
615 setup_range(&range_attrs, &ops, &i, start, stop, CSA_TYPE_EVENT,
616 0, B_FALSE, version);
617 status = csa_list_entries(target, i, range_attrs, ops, &a_total, list, NULL);
618 free_range(&range_attrs, &ops, i);
620 appt = allocate_appt_struct(appt_read,
622 CSA_ENTRY_ATTR_START_DATE_I,
623 CSA_ENTRY_ATTR_SUMMARY_I,
624 CSA_X_DT_ENTRY_ATTR_SHOWTIME_I,
625 CSA_ENTRY_ATTR_END_DATE_I,
627 for (i = 0; i < a_total; i++) {
629 if (query_appt_struct(target, (*list)[i], appt) != CSA_SUCCESS) {
630 mini_err_msg(catd, appt->what->value->item.string_value,
635 _csa_iso8601_to_tick(appt->time->value->item.date_time_value, &start_tick);
637 _csa_iso8601_to_tick(appt->end_time->value->item.\
638 date_time_value, &end_tick);
639 day = dom(start_tick);
640 if (day != last_day) {
641 cm_tty_format_header(p, start_tick, date_str);
642 fprintf(stdout, "\n%s %s:\n",
643 catgets(catd, 1, 1068, "Appointments for"),
648 memset(buf, '\0', MAXNAMELEN);
649 if (appt->show_time->value->item.sint32_value &&
650 !magic_time(start_tick)) {
651 dt = get_int_prop(p, CP_DEFAULTDISP);
652 format_time(start_tick, dt, start_buf);
654 format_time(end_tick, dt, end_buf);
657 sprintf(buf, "%s%c%7s ", start_buf,
658 (*end_buf ? '-' : ' '), end_buf);
661 fprintf(stdout, "\t%3d) %s", lineno++, buf);
662 if ((lines = text_to_lines(appt->what->value->item.string_value, 5)) && lines->s) {
664 fprintf(stdout, "%s\n", lines->s);
665 next_line = lines->next;
668 "\t%21s%s\n", " ", next_line->s);
669 next_line = next_line->next;
672 fprintf(stdout, "\n");
674 destroy_lines(lines);
675 fprintf(stdout, "\n");
677 free_appt_struct(&appt);
680 cm_tty_format_header(p, start + 1, date_str);
681 fprintf(stdout, "\n%s %s\n\n",
682 catgets(catd, 1, 1069, "No Appointments for"),
689 * These functions will convert a string to the enumerated value
692 convert_boolean_str(char *val) {
693 if (strncasecmp(val, "T", 1) == 0 || strcasecmp(val, "B_TRUE") == 0)
699 convert_privacy_str_to_op(char *val) {
704 * i defaults to 1 = CSA_CLASS_PRIVATE, so no need to check for that
707 if (strcmp(val, privacy_str(0)) == 0 ||
708 strcmp(val, privacy_str_old(0)) == 0 ||
709 strcmp(val, privacy_str_411(0)) == 0)
711 else if (strcmp(val, privacy_str(1)) == 0 ||
712 strcmp(val, privacy_str_old(1)) == 0 ||
713 strcmp(val, privacy_str_411(1)) == 0)
720 convert_privacy_str(char *val) {
721 CSA_sint32 ret_val = CSA_CLASS_PRIVATE;
723 if ((strcasecmp(val, privacy_str(0)) == 0) ||
724 (strcasecmp(val, privacy_str_old(0)) == 0) ||
725 (strcasecmp(val, privacy_str_411(0)) == 0))
726 ret_val = CSA_CLASS_PUBLIC;
727 else if ((strcasecmp(val, privacy_str(1)) == 0) ||
728 (strcasecmp(val, privacy_str_old(1)) == 0) ||
729 (strcasecmp(val, privacy_str_411(1)) == 0))
730 ret_val = CSA_CLASS_CONFIDENTIAL;
731 else if ((strcasecmp(val, privacy_str(2)) == 0) ||
732 (strcasecmp(val, privacy_str_old(2)) == 0) ||
733 (strcasecmp(val, privacy_str_411(2)) == 0))
734 ret_val = CSA_CLASS_PRIVATE;
740 convert_separator_str(char *val) {
741 SeparatorType op = SEPARATOR_BLANK;
742 char *search_val = separator_str(op);
744 while (search_val && strcasecmp(search_val, val) != 0)
745 search_val = separator_str(++op);
749 extern Time_scope_menu_op
750 convert_time_scope_str(char *val) {
751 Time_scope_menu_op op = TIME_MINS;
752 char *search_val = time_scope_str(op);
754 while(search_val && strcasecmp(search_val, val) != 0)
755 search_val = time_scope_str(++op);
760 day_str(Days_op op) {
761 if (op >= SUNDAY && op <= SATURDAY)
767 default_repeat_cnt_str(Repeat_menu_op op) {
769 if (op >= ONE_TIME && op <= REPEAT_EVERY)
770 return default_repeat_cnt_strs[op];
775 default_repeat_scope_str(
779 if (!default_repeat_scope_strs[DAILY]) {
780 default_repeat_scope_strs[ONE_TIME] = strdup("\0");
781 default_repeat_scope_strs[DAILY] =
782 strdup(catgets(catd, 1, 994, "days"));
783 default_repeat_scope_strs[WEEKLY] =
784 strdup(catgets(catd, 1, 995, "weeks"));
785 default_repeat_scope_strs[EVERY_TWO_WEEKS] =
786 strdup(catgets(catd, 1, 996, "biweeks"));
787 default_repeat_scope_strs[MONTHLY_BY_DATE] =
788 strdup(catgets(catd, 1, 997, "months"));
789 default_repeat_scope_strs[MONTHLY_BY_WEEKDAY] =
790 default_repeat_scope_strs[MONTHLY_BY_DATE];
791 default_repeat_scope_strs[YEARLY] =
792 strdup(catgets(catd, 1, 998, "years"));
793 default_repeat_scope_strs[MONDAY_THRU_FRIDAY] =
794 default_repeat_scope_strs[WEEKLY];
795 default_repeat_scope_strs[MON_WED_FRI] =
796 default_repeat_scope_strs[WEEKLY];
797 default_repeat_scope_strs[TUESDAY_THURSDAY] =
798 default_repeat_scope_strs[WEEKLY];
799 default_repeat_scope_strs[REPEAT_EVERY] = strdup("\0");
802 if (op >= ONE_TIME && op <= REPEAT_EVERY)
803 return default_repeat_scope_strs[op];
808 for_str(For_menu_op op) {
809 if (op >= TWO && op <= FOR_EVER)
815 ** Return a date label according to order and sep
818 get_datemsg(OrderingType order, SeparatorType sep) {
819 char *str = separator_str(sep);
824 sprintf(buf, "%s %s %s %s %s", "Day", str, "Month", str, "Year");
827 sprintf(buf, "%s %s %s %s %s", "Year", str, "Month", str, "Day");
831 sprintf(buf, "%s %s %s %s %s", "Month", str, "Day", str, "Year");
834 return(cm_strdup(buf));
838 * This function is used by the appointment file parsing routines to return
839 * whether or not the value in the passed buffer is a key word
842 identify_parse_key(char *str) {
844 if (!new_appt_begin_delimiter) {
845 new_appt_begin_delimiter = malloc(strlen(CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER) + 14);
846 sprintf(new_appt_begin_delimiter, "%s%s",
847 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
851 if (strncasecmp(str, "** Calendar Appointment **",
852 cm_strlen("** Calendar Appointment **")) == 0)
853 return APPOINTMENT_START;
854 if (strncasecmp(str, "date:", cm_strlen("date:")) == 0)
856 if (strncasecmp(str, "time:", cm_strlen("time:")) == 0 ||
857 strncasecmp(str, "start:", cm_strlen("start:")) == 0 ||
858 strncasecmp(str, "from:", cm_strlen("from:")) == 0)
860 if(strncasecmp(str, "end:", cm_strlen("end:")) == 0 ||
861 strncasecmp(str, "until:", cm_strlen("until:")) == 0 ||
862 strncasecmp(str, "stop:", cm_strlen("stop:")) == 0 ||
863 strncasecmp(str, "to:", cm_strlen("to:")) == 0)
865 if (strncasecmp(str, "duration:", cm_strlen("duration:")) == 0)
867 if (strncasecmp(str, "what:", cm_strlen("what:")) == 0)
869 if (strncasecmp(str, "repeat:", cm_strlen("repeat:")) == 0)
871 if (strncasecmp(str, "for:", cm_strlen("for:")) == 0)
873 if (strncasecmp(str, new_appt_begin_delimiter, cm_strlen(new_appt_begin_delimiter)) == 0)
880 * This function will fill in the default values using the properties database
881 * for a given appointment
884 load_appt_defaults(Dtcm_appointment *a, Props *p) {
885 a->type->value->item.sint32_value = CSA_TYPE_EVENT;
886 a->subtype->value->item.string_value = strdup(CSA_SUBTYPE_APPOINTMENT);
887 a->state->value->item.sint32_value = CSA_X_DT_STATUS_ACTIVE;
888 if (a->repeat_type && a->repeat_type->value)
889 a->repeat_type->value->item.sint32_value = CSA_X_DT_REPEAT_ONETIME;
890 if (a->repeat_times && a->repeat_times->value)
891 a->repeat_times->value->item.uint32_value = 0;
892 a->private->value->item.sint32_value =
893 convert_privacy_str(get_char_prop(p, CP_PRIVACY));
895 load_reminder_props(a, p);
899 load_reminder_props(Dtcm_appointment *a, Props *p) {
900 if (convert_boolean_str(get_char_prop(p, CP_BEEPON))) {
901 a->beep->value->item.reminder_value->lead_time = malloc(BUFSIZ);
902 _csa_duration_to_iso8601(get_int_prop(p, CP_BEEPADV) *
903 timescopestring_to_tick(get_char_prop(p, CP_BEEPUNIT)),
904 a->beep->value->item.reminder_value->lead_time);
905 a->beep->value->item.reminder_value->reminder_data.data = NULL;
906 a->beep->value->item.reminder_value->reminder_data.size = 0;
908 if (convert_boolean_str(get_char_prop(p, CP_FLASHON))) {
909 a->flash->value->item.reminder_value->lead_time = malloc(BUFSIZ);
910 _csa_duration_to_iso8601(get_int_prop(p, CP_FLASHADV) *
911 timescopestring_to_tick(get_char_prop(p, CP_FLASHUNIT)),
912 a->flash->value->item.reminder_value->lead_time);
913 a->flash->value->item.reminder_value->reminder_data.data = NULL;
914 a->flash->value->item.reminder_value->reminder_data.size = 0;
916 if (convert_boolean_str(get_char_prop(p, CP_OPENON))) {
917 a->popup->value->item.reminder_value->lead_time = malloc(BUFSIZ);
918 _csa_duration_to_iso8601(get_int_prop(p, CP_OPENADV) *
919 timescopestring_to_tick(get_char_prop(p, CP_OPENUNIT)),
920 a->popup->value->item.reminder_value->lead_time);
921 a->popup->value->item.reminder_value->reminder_data.data = NULL;
922 a->popup->value->item.reminder_value->reminder_data.size = 0;
924 if (convert_boolean_str(get_char_prop(p, CP_MAILON))) {
925 a->mail->value->item.reminder_value->lead_time = malloc(BUFSIZ);
926 _csa_duration_to_iso8601(get_int_prop(p, CP_MAILADV) *
927 timescopestring_to_tick(get_char_prop(p, CP_MAILUNIT)),
928 a->mail->value->item.reminder_value->lead_time);
929 a->mail->value->item.reminder_value->reminder_data.data = (CSA_uint8 *) strdup(get_char_prop(p, CP_MAILTO));
930 a->mail->value->item.reminder_value->reminder_data.size = strlen(get_char_prop(p, CP_MAILTO)) + 1;
935 month_str(Months_op op) {
936 if (op >= JANUARY && op <= DECEMBER)
937 return month_strs[op];
942 build_new_attrval(CSA_attribute *attrval, char *name, char *tag, char *value)
945 CSA_attribute_value *vptr = calloc(sizeof(CSA_attribute_value), 1);
947 CSA_access_list l_ptr;
948 boolean_t done = B_FALSE;
949 CSA_access_list *link_location;
951 attrval->name = cm_strdup(name);
952 attrval->value = vptr;
954 if (!strcmp(tag, "string")) {
955 vptr->type = CSA_VALUE_STRING;
956 vptr->item.string_value = cm_strdup(value);
959 if (!strcmp(tag, "datetime")) {
960 vptr->type = CSA_VALUE_DATE_TIME;
961 vptr->item.date_time_value = cm_strdup(value);
964 if (!strcmp(tag, "caluser")) {
965 vptr->type = CSA_VALUE_CALENDAR_USER;
966 s_ptr = strchr(value, ':');
968 vptr->item.calendar_user_value = calloc(sizeof(CSA_calendar_user), 1);
969 vptr->item.calendar_user_value->user_name = cm_strdup(s_ptr + 1);
970 vptr->item.calendar_user_value->user_type = atoi(value);
973 if (!strcmp(tag, "uinteger")) {
974 vptr->type = CSA_VALUE_UINT32;
975 vptr->item.sint32_value = atoi(value);
978 if (!strcmp(tag, "sinteger")) {
979 vptr->type = CSA_VALUE_SINT32;
980 vptr->item.sint32_value = atoi(value);
983 if (!strcmp(tag, "reminder")) {
984 vptr->type = CSA_VALUE_REMINDER;
985 s_ptr = strchr(value, ':');
988 vptr->item.reminder_value = calloc(sizeof(CSA_reminder), 1);
990 vptr->item.reminder_value->lead_time = malloc(BUFSIZ);
991 _csa_duration_to_iso8601(atoi(value), vptr->item.reminder_value->lead_time);
992 vptr->item.reminder_value->reminder_data.data = (CSA_uint8 *) strdup(s_ptr + 1);
993 vptr->item.reminder_value->reminder_data.size = strlen(s_ptr + 1) + 1;
996 if (!strcmp(tag, "accesslist")) {
998 /* The access list format is that each member in the
999 list is written out on a separate line. So after
1000 the initial newline in the value, the pattern is a
1001 TAB followed by the mask value followed by a colon,
1002 followed by the string. */
1004 vptr->type = CSA_VALUE_ACCESS_LIST;
1005 link_location = &vptr->item.access_list_value;
1007 if (value && *b_ptr) {
1009 /* we have an initial value. */
1012 l_ptr = calloc(sizeof(CSA_access_rights), 1);
1014 s_ptr = strchr(b_ptr, ':');
1017 l_ptr->rights = atoi(b_ptr);
1020 if (s_ptr = strchr(b_ptr, '\n'))
1023 l_ptr->user->user_name = cm_strdup(b_ptr);
1030 *link_location = l_ptr;
1031 link_location = &l_ptr->next;
1040 read_new_appt(FILE *fp, Dtcm_appointment **appt, Props *p, int version)
1043 int attrs_allocated, attrs_written = 0;
1044 char line[MAXNAMELEN], *b_ptr, *c_ptr;
1045 char *a_name = NULL, *a_tag = NULL, *a_value = NULL;
1046 Dtcm_appointment *avlist;
1048 if (!new_appt_end_delimiter) {
1049 new_appt_end_delimiter = malloc(strlen(CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER) + 14);
1050 sprintf(new_appt_end_delimiter, "%s%s",
1051 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
1054 avlist = allocate_appt_struct(appt_write, DATAVER_ARCHIVE, NULL);
1055 load_appt_defaults(avlist, p);
1057 /* At this point, we really want to negate all of the links set
1058 up by tha appointment allocation routine. The following code is
1059 almost certain to invalidate them, and some trailing links would
1062 avlist->identifier = NULL;
1063 avlist->modified_time = NULL;
1064 avlist->author = NULL;
1065 avlist->time = NULL;
1066 avlist->type = NULL;
1067 avlist->subtype = NULL;
1068 avlist->private = NULL;
1069 avlist->end_time = NULL;
1070 avlist->show_time = NULL;
1071 avlist->what = NULL;
1072 avlist->state = NULL;
1073 avlist->repeat_type = NULL;
1074 avlist->repeat_times = NULL;
1075 avlist->repeat_interval = NULL;
1076 avlist->repeat_week_num = NULL;
1077 avlist->recurrence_rule = NULL;
1078 avlist->beep = NULL;
1079 avlist->flash = NULL;
1080 avlist->mail = NULL;
1081 avlist->popup = NULL;
1083 attrs_allocated = avlist->count;
1085 /* should be starting a new attribute definition */
1087 while (fgets(line, MAXNAMELEN - 1, fp))
1090 /* look for new end marker on appointment */
1092 if (strncmp(line, new_appt_end_delimiter, strlen(new_appt_end_delimiter)) == 0)
1095 if (line[0] == '\t') {
1097 /* This is a continuation line from the previous
1098 attribute definition. The value here should
1099 be catenated onto the previously yanked value */
1103 /* a line with only a tab on it, and no text
1104 means that a newline should be inserted
1110 a_value = realloc(a_value, strlen(a_value) +
1112 strcat(a_value, "\n");
1113 strcat(a_value, b_ptr);
1115 a_value[strlen(a_value) - 1] = '\0';
1119 else if (line[0] == '\n') {
1121 /* An empty line. This means the end of
1122 the appointment definition */
1127 /* if the line is neither a termination line, nor
1128 a continuation line, then the entry must be a new
1129 attribute name. We should declare the previous
1132 if (a_name && a_tag && a_value)
1135 /* see if the allocated buffer is large enough to
1136 contain another attibute. If not, make it a bit
1137 larger, and then copy the attribute. */
1139 if (ident_name_ro(a_name, DATAVER_ARCHIVE) == B_FALSE) {
1140 if ((attrs_written) == attrs_allocated) {
1141 attrs_allocated += 10;
1142 avlist->attrs = realloc(avlist->attrs,
1143 sizeof(CSA_attribute) *
1147 build_new_attrval(&avlist->attrs[attrs_written],
1148 a_name, a_tag, a_value);
1157 /* this should pull off the new attribute name */
1159 a_name = a_tag = a_value = NULL;
1162 if (c_ptr = strchr(line, ':'))
1165 a_name = cm_strdup(b_ptr);
1168 /* big problem. Malformed attribute specification */
1176 if (c_ptr = strchr(b_ptr, ':'))
1179 a_tag = cm_strdup(b_ptr);
1182 /* big problem. Malformed attribute specification */
1190 b_ptr[strlen(b_ptr) - 1] = '\0';
1192 a_value = cm_strdup(b_ptr);
1195 /* finished due to end of file. Early termination, but not too bad */
1197 if (a_name && a_tag && a_value)
1200 /* see if the allocated buffer is large enough to
1201 contain another attibute. If not, make it a bit
1202 larger, and then copy the attribute. */
1204 if (ident_name_ro(a_name, DATAVER_ARCHIVE) == B_FALSE) {
1205 if ((attrs_written) == attrs_allocated) {
1206 attrs_allocated += 10;
1207 avlist->attrs = realloc(avlist->attrs,
1208 sizeof(CSA_attribute) *
1212 build_new_attrval(&avlist->attrs[attrs_written],
1213 a_name, a_tag, a_value);
1223 avlist->count = attrs_written;
1224 set_appt_links(avlist);
1233 switch (dow(tick)) {
1235 return (cm_strdup("SU"));
1237 return (cm_strdup("MO"));
1239 return (cm_strdup("TU"));
1241 return (cm_strdup("WE"));
1243 return (cm_strdup("TH"));
1245 return (cm_strdup("FR"));
1248 return (cm_strdup("SA"));
1252 /* this routine is designed to take in an appintment, and generate the
1253 DATAVER4 recurrence rule for that appointment. It is also responsible
1254 for crushing out the old style attributes that may not be inserted
1255 into a newer style data model. */
1258 generate_recurrence_rule(Dtcm_appointment *appt, int version) {
1263 CSA_sint32 repeat_type;
1264 CSA_uint32 repeat_nth = 0;
1265 CSA_uint32 repeat_for = 0;
1269 rule_buf1[0] = '\0';
1270 rule_buf2[0] = '\0';
1272 if (appt->repeat_type && appt->repeat_type->value)
1273 repeat_type = appt->repeat_type->value->item.sint32_value;
1275 repeat_type = CSA_X_DT_REPEAT_ONETIME;
1277 if (appt->repeat_interval && appt->repeat_interval->value)
1278 repeat_nth = appt->repeat_interval->value->item.uint32_value;
1280 if (appt->repeat_times && appt->repeat_times->value)
1281 repeat_for = appt->repeat_times->value->item.uint32_value;
1283 _csa_iso8601_to_tick(appt->time->value->item.date_time_value, &appt_time);
1285 if (!appt->recurrence_rule || !appt->recurrence_rule->value) {
1286 switch (repeat_type) {
1288 case CSA_X_DT_REPEAT_ONETIME :
1289 strcpy(rule_buf1, "D1 ");
1292 case CSA_X_DT_REPEAT_DAILY :
1293 strcpy(rule_buf1, "D1 ");
1296 case CSA_X_DT_REPEAT_WEEKLY :
1297 strcpy(rule_buf1, "W1 ");
1300 case CSA_X_DT_REPEAT_BIWEEKLY :
1301 strcpy(rule_buf1, "W2 ");
1304 case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY :
1306 if (weekofmonth(appt_time, &wk) && wk == 5)
1307 sprintf(rule_buf1, "MP1 1- %s ", dow_str(appt_time));
1309 strcpy(rule_buf1, "MP1 ");
1313 case CSA_X_DT_REPEAT_MONTHLY_BY_DATE :
1314 strcpy(rule_buf1, "MD1 ");
1317 case CSA_X_DT_REPEAT_YEARLY :
1318 strcpy(rule_buf1, "YM1 ");
1321 case CSA_X_DT_REPEAT_MON_TO_FRI :
1322 strcpy(rule_buf1, "W1 MO TU WE TH FR ");
1325 case CSA_X_DT_REPEAT_MONWEDFRI :
1326 strcpy(rule_buf1, "W1 MO WE FR ");
1329 case CSA_X_DT_REPEAT_TUETHUR :
1330 strcpy(rule_buf1, "W1 TU TH ");
1333 case CSA_X_DT_REPEAT_EVERY_NDAY :
1334 sprintf(rule_buf1, "D%ld ", repeat_nth);
1337 case CSA_X_DT_REPEAT_EVERY_NWEEK :
1338 sprintf(rule_buf1, "W%ld ", repeat_nth);
1341 case CSA_X_DT_REPEAT_EVERY_NMONTH :
1342 sprintf(rule_buf1, "MD%ld ", repeat_nth);
1346 if (repeat_for == 0)
1347 strcat(rule_buf2, "#1");
1349 sprintf(rule_buf2, "#%ld", repeat_for);
1351 strcat (rule_buf1, rule_buf2);
1353 appt->attrs = realloc(appt->attrs,
1354 sizeof(CSA_attribute) * (appt->count + 1));
1356 build_new_attrval(&appt->attrs[appt->count],
1357 CSA_ENTRY_ATTR_RECURRENCE_RULE,
1363 set_appt_links(appt);
1366 if (version == DATAVER_ARCHIVE)
1369 /* crush out the old values, if they exist */
1371 if (appt->repeat_type && appt->repeat_type->name) {
1372 free(appt->repeat_type->name);
1373 appt->repeat_type->name = NULL;
1376 if (appt->repeat_times && appt->repeat_times->name) {
1377 free(appt->repeat_times->name);
1378 appt->repeat_times->name = NULL;
1381 if (appt->repeat_interval && appt->repeat_interval->name) {
1382 free(appt->repeat_interval->name);
1383 appt->repeat_interval->name = NULL;
1386 if (appt->repeat_week_num && appt->repeat_week_num->name) {
1387 free(appt->repeat_week_num->name);
1388 appt->repeat_week_num->name = NULL;
1394 * Given a file name, we're going to parse the file and create an appointment
1395 * for the calling routine.
1397 * This function will scan for X number of appointments in the file and add
1398 * each to the linked list unless the validation routine returns an error.
1399 * If this happens, the invalid appointment is still added to the list so the
1400 * calling routine can do further processing if necessary, but the remaining
1401 * appointments in the file (if any) are not read.
1404 parse_appt_from_file(nl_catd catd, char *file, CmDataList *list, Props *p,
1405 boolean_t(*query)(), void *key_data, int version) {
1407 char line[MAXNAMELEN], *tmp, *key_str, *val_str,
1408 s_buf[MAXNAMELEN], e_buf[MAXNAMELEN],
1409 d_buf[MAXNAMELEN], r_buf[MAXNAMELEN],
1410 f_buf[MAXNAMELEN], *w_buf = NULL;
1412 boolean_t processing_appt = B_FALSE,
1413 processing_what = B_FALSE;
1415 Validate_op valid_op = VALID_APPT;
1416 Parse_key_op pko = NOT_A_KEY;
1417 Dtcm_appointment *appt;
1418 boolean_t found_appt = B_FALSE;
1420 if (!file || *file == '\0' || (fp = fopen(file, "r")) == NULL)
1421 return COULD_NOT_OPEN_FILE;
1423 appt = allocate_appt_struct(appt_write, version, NULL);
1424 load_appt_defaults(appt, p);
1425 while (valid_op == VALID_APPT && fgets(line, MAXNAMELEN - 1, fp)) {
1427 * Point key_str at the first non-space character
1431 if ((*line != ' ' && *line != '\t') || blank_buf(line)) {
1432 processing_what = B_FALSE;
1437 * The check for '*' handles the case when the appt header
1438 * ``** Calendar Appointment **'' has no white space to its
1441 if (*key_str == '\0' ||
1442 (!isspace((u_char)*key_str) && (u_char)*key_str != '*'))
1445 while (*key_str != '\0' && isspace((u_char)*key_str))
1448 pko = identify_parse_key(key_str);
1451 * Remove any ending white space
1453 tmp = strrchr(line, '\0'); --tmp;
1454 while(isspace((u_char)*tmp)) {
1460 * If we're not currently processing the what string and we
1461 * haven't identified a key, continue the loop. Otherwise,
1462 * we've identified a key, so we can't be in the middle of
1463 * processing the what string -- set the flag accordingly.
1466 if (pko == NOT_A_KEY) {
1467 if (!processing_what || what_lines >= 5)
1472 processing_what = B_FALSE;
1477 * Point val_str at the next non-space character after the key
1480 while(*val_str != '\0' && !isspace((u_char)*val_str))
1482 while(*val_str != '\0' && isspace((u_char)*val_str))
1486 * Now, based on the keyword, set the necessary stuff in the
1487 * new appointment structure.
1490 case APPOINTMENT_START:
1491 if (processing_appt) {
1493 * We've reached another appointment. Add the
1494 * current to the linked list, reset flags, and
1495 * check it's validity ...
1497 CmDataListAdd(list, (void *)appt, 0);
1499 valid_op = validate_appt(catd, appt, s_buf,
1500 e_buf, d_buf, dur, w_buf, r_buf, f_buf,
1501 query, key_data, version);
1502 if (valid_op == VALID_APPT) {
1503 appt = allocate_appt_struct(appt_write, version, NULL);
1504 load_appt_defaults(appt, p);
1507 processing_appt = B_FALSE;
1509 processing_appt = B_TRUE;
1511 found_appt = B_TRUE;
1514 memset(s_buf, '\0', MAXNAMELEN);
1515 memset(e_buf, '\0', MAXNAMELEN);
1516 memset(d_buf, '\0', MAXNAMELEN);
1517 memset(r_buf, '\0', MAXNAMELEN);
1518 memset(f_buf, '\0', MAXNAMELEN);
1524 cm_strcpy(d_buf, val_str);
1527 cm_strcpy(s_buf, val_str);
1528 len = cm_strlen(s_buf) - 1;
1529 if (s_buf[len] == 'a' || s_buf[len] == 'p')
1530 cm_strcat(s_buf, "m");
1533 cm_strcpy(e_buf, val_str);
1534 len = cm_strlen(e_buf) - 1;
1535 if (e_buf[len] == 'a' || e_buf[len] == 'p')
1536 cm_strcat(e_buf, "m");
1539 dur = atoi(val_str);
1542 * Check for a unit specification
1544 while(*val_str != '\0' && !isspace((u_char)*val_str))
1546 while(*val_str != '\0' && isspace((u_char)*val_str))
1570 * If we're not currently processing a what string
1571 * and we've got a what value, there were more than one
1572 * what strings in the file - delete the first one and
1575 * Otherwise, we in process, so alloc more space and
1576 * concatinate the new string to the end of the last.
1578 if (!processing_what) {
1581 w_buf = cm_strdup(val_str);
1585 w_buf = (char *)ckalloc(cm_strlen(tmp)
1586 + cm_strlen(key_str) + 2);
1587 cm_strcpy(w_buf, tmp);
1588 cm_strcat(w_buf, "\n");
1589 cm_strcat(w_buf, key_str);
1594 processing_what = B_TRUE;
1597 cm_strcpy(r_buf, val_str);
1600 cm_strcpy(f_buf, val_str);
1603 if ((version >= DATAVER4) ||
1604 (version == DATAVER_ARCHIVE)) {
1605 read_new_appt(fp, &appt, p, version);
1606 generate_recurrence_rule(appt, version);
1607 CmDataListAdd(list, (void *)appt, 0);
1608 processing_appt = B_FALSE;
1609 found_appt = B_TRUE;
1619 * Because the appointment read is only saved when we start reading the
1620 * next appointment, the last appointment (which doesn't have a next)
1621 * will always be left off ... so, providing we're processing an
1622 * appointment -- meaning we've found at least one header -- save the
1623 * appointment in process.
1626 if (found_appt == B_FALSE) {
1629 return(INVALID_DATE);
1632 if (processing_appt) {
1633 CmDataListAdd(list, (void *)appt, 0);
1634 valid_op = validate_appt(catd, appt, s_buf, e_buf, d_buf, dur,
1635 w_buf, r_buf, f_buf, query, key_data,
1642 scrub_attr_list(appt);
1649 growcat(char **source, char *new)
1651 *source = (char *) realloc(*source, strlen(*source) + strlen(new) + 2);
1652 strcat(*source, new);
1656 cat_indented_string(char **catbuf, char *string)
1658 char *p_str = string;
1663 nl_count = count_newlines(string);
1665 b_ptr = buf = malloc(strlen(string) + nl_count + 1);
1675 growcat(catbuf, buf);
1681 attrs_to_string(CSA_attribute * attrs, int num_attrs)
1684 CSA_access_list a_ptr;
1685 char *buffer = malloc(1);
1686 char tmp_buf[MAXNAMELEN];
1690 for (i = 0; i < num_attrs; i++) {
1691 if (!attrs[i].name || (attrs[i].value == NULL))
1695 sprintf(tmp_buf, "%s:", attrs[i].name);
1696 switch (attrs[i].value->type) {
1697 case CSA_VALUE_SINT32:
1698 growcat(&buffer, tmp_buf);
1699 sprintf(tmp_buf, "sinteger:%ld\n",
1700 attrs[i].value->item.sint32_value);
1701 growcat(&buffer, tmp_buf);
1704 case CSA_VALUE_UINT32:
1705 growcat(&buffer, tmp_buf);
1706 sprintf(tmp_buf, "uinteger:%ld\n",
1707 attrs[i].value->item.uint32_value);
1708 growcat(&buffer, tmp_buf);
1711 case CSA_VALUE_DATE_TIME:
1712 if (attrs[i].value->item.string_value == NULL)
1715 growcat(&buffer, tmp_buf);
1716 growcat(&buffer, "datetime:");
1717 if (attrs[i].value->item.date_time_value)
1718 cat_indented_string(&buffer,
1719 attrs[i].value->item.string_value);
1720 growcat(&buffer, "\n");
1723 case CSA_VALUE_STRING:
1724 if (attrs[i].value->item.string_value == NULL)
1727 growcat(&buffer, tmp_buf);
1728 growcat(&buffer, "string:");
1729 if (attrs[i].value->item.string_value)
1730 cat_indented_string(&buffer,
1731 attrs[i].value->item.string_value);
1732 growcat(&buffer, "\n");
1734 case CSA_VALUE_CALENDAR_USER:
1735 if (attrs[i].value->item.calendar_user_value == NULL)
1738 growcat(&buffer, tmp_buf);
1739 sprintf(tmp_buf, "caluser:%ld:", attrs[i].value->item.calendar_user_value->user_type);
1740 growcat(&buffer, tmp_buf);
1741 if (attrs[i].value->item.calendar_user_value->user_name)
1742 cat_indented_string(&buffer,
1743 attrs[i].value->item.calendar_user_value->user_name);
1745 growcat(&buffer, "\n");
1748 case CSA_VALUE_REMINDER:
1749 if (attrs[i].value->item.reminder_value->lead_time == NULL)
1752 growcat(&buffer, tmp_buf);
1753 _csa_iso8601_to_duration(attrs[i].value->item.reminder_value->lead_time, &advance_time);
1754 sprintf(tmp_buf, "reminder:%d:", advance_time);
1755 growcat(&buffer, tmp_buf);
1756 if (attrs[i].value->item.reminder_value->reminder_data.data)
1757 cat_indented_string(&buffer,
1758 (char *) attrs[i].value->item.reminder_value->reminder_data.data);
1759 growcat(&buffer, "\n");
1762 case CSA_VALUE_ACCESS_LIST:
1763 if (attrs[i].value->item.access_list_value == NULL)
1766 growcat(&buffer, tmp_buf);
1767 growcat(&buffer, "accesslist:\n");
1768 a_ptr = attrs[i].value->item.access_list_value;
1770 sprintf(tmp_buf, "\t%d:%s\n",
1771 (int) a_ptr->rights, a_ptr->user->user_name);
1772 growcat(&buffer, tmp_buf);
1773 a_ptr = a_ptr->next;
1783 entry_to_attrval_string(CSA_session_handle target, CSA_entry_handle entry)
1787 CSA_attribute_reference *names;
1788 CSA_attribute *attrs_ret;
1789 CSA_uint32 num_attrs, num_attrs_ret;
1791 csa_list_entry_attributes(target, entry, &num_attrs, &names, NULL);
1792 csa_read_entry_attributes(target, entry, num_attrs, names, &num_attrs_ret,
1795 ptr = attrs_to_string(attrs_ret, num_attrs_ret);
1798 ** Need to check that library's memory management will
1799 ** properly free this. Old scheme had a special fn to
1800 ** to it, which took a pointer and a count. - dac
1802 ** [old version: DtCmFreeAttributeValues(attrs, num_attrs); ]
1804 csa_free(attrs_ret);
1811 calendar_to_attrval_string(CSA_session_handle cal)
1814 CSA_uint32 num_attrs, num_attrs_ret;
1815 CSA_attribute_reference *names;
1817 CSA_attribute *attrs_ret;
1819 csa_list_calendar_attributes(cal, &num_attrs, &names, NULL);
1820 csa_read_calendar_attributes(cal, num_attrs, names, &num_attrs_ret,
1823 ptr = attrs_to_string(attrs_ret, num_attrs_ret);
1825 csa_free(attrs_ret);
1832 * Given an appointment structure, return a character string that can be used
1833 * for DnD or written to a file.
1836 parse_appt_to_string(CSA_session_handle target, CSA_entry_handle entry, Props *p, int version) {
1837 char *ret_val, *attr_string;
1838 Dtcm_appointment *appt;
1839 CSA_uint32 na_ret; /* num of attributes actually read */
1840 CSA_attribute a_ret; /* list of attrs actually read */
1842 attr_string = entry_to_attrval_string(target, entry);
1845 * Extract the info we need from the back-end entry
1847 appt = allocate_appt_struct(appt_read,
1851 query_appt_struct(target, entry, appt);
1852 ret_val = parse_attrs_to_string(appt, p, attr_string);
1853 free_appt_struct(&appt);
1860 parse_attrs_to_string(Dtcm_appointment *appt, Props *p, char *attr_string) {
1861 int nlcount, duration, repeat_nth, repeat_wk, wk;
1862 char *whatstr, d_buf[MAXNAMELEN],
1863 s_buf[MAXNAMELEN], e_buf[MAXNAMELEN], w_buf[MAXNAMELEN],
1864 r_buf[MAXNAMELEN], f_buf[MAXNAMELEN], *appt_what,
1866 time_t tick, end_tick = 0;
1867 CSA_sint32 repeat_type = 0;
1868 CSA_uint32 repeat_times;
1869 static char *format_string = "\n\n\t** Calendar Appointment **\n%s:string:begin\n%s%s:string:end\n\tDate: %s\n\tStart: %s\n\tEnd: %s\n\tRepeat: %s\n\tFor: %s\n\tWhat: %s";
1877 _csa_iso8601_to_tick(appt->time->value->item.date_time_value, &tick);
1879 if (appt->end_time->value != NULL)
1880 _csa_iso8601_to_tick(appt->end_time->value->item.\
1881 date_time_value, &end_tick);
1883 appt_what = appt->what->value->item.string_value;
1885 if (appt->repeat_type && appt->repeat_type->value)
1886 repeat_type = appt->repeat_type->value->item.sint32_value;
1888 if (appt->repeat_times && appt->repeat_times->value)
1889 repeat_times = appt->repeat_times->value->item.uint32_value;
1893 if (appt->repeat_interval && appt->repeat_interval->value)
1894 repeat_nth = appt->repeat_interval->value->item.uint32_value;
1898 if (appt->repeat_week_num && appt->repeat_week_num->value)
1899 repeat_wk = appt->repeat_week_num->value->item.sint32_value;
1904 * Format the date and start/stop strings
1906 format_tick(tick, ORDER_MDY, SEPARATOR_SLASH, d_buf);
1907 format_time(tick, get_int_prop(p, CP_DEFAULTDISP), s_buf);
1908 format_time(end_tick, get_int_prop(p, CP_DEFAULTDISP), e_buf);
1911 * Handle the what string
1913 whatstr = appt_what;
1914 if ((nlcount = count_newlines(appt_what)) > 0) {
1915 whatstr = (char *)ckalloc(cm_strlen(appt_what) + nlcount + 1);
1916 copy_and_pad_newlines(whatstr, appt_what);
1918 if (whatstr && !blank_buf(whatstr)) {
1919 cm_strcat(w_buf, whatstr);
1920 cm_strcat(w_buf, "\n\t");
1924 * Repeat and For stuff
1926 cm_strcpy(r_buf, periodstr_from_period(repeat_type, repeat_nth));
1927 if (repeat_type == CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY) {
1928 if (weekofmonth(tick, &wk) && wk == 4) {
1929 if (repeat_wk == -1)
1930 strcat(r_buf, ", last");
1931 else if (repeat_wk == 4)
1932 strcat(r_buf, ", 4th");
1936 sprintf(f_buf, "%ld", repeat_times);
1939 * Put it all together
1942 b_ptr = malloc(cm_strlen(d_buf) +
1943 (2 * cm_strlen(CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER)) +
1949 cm_strlen(format_string) +
1950 cm_strlen(attr_string) +
1953 sprintf(b_ptr, format_string,
1954 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
1956 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
1957 d_buf, s_buf, e_buf, r_buf, f_buf, w_buf);
1964 /* This routine takes a list of buffers that represent a number
1965 of message attachments, and glues them into a mime format
1966 message for eventual submission to the dtmail mailer. These
1967 objects do nor *need* to be cm appointment objects. */
1970 create_rfc_message(char *address_list,
1972 char **appointment_objects,
1977 /* do *not* put these header strings in a message catalog.
1978 These are invariants specified by MIME */
1980 char *address_header = "To: ";
1981 char *subject_header = "Subject: ";
1982 char *x_content_name = "X-Content-Name: CalendarAppointment\n";
1983 char *content_label = "Mime-Version: 1.0\nContent-Type: multipart/mixed;boundary=";
1984 char divider_string[32];
1988 boolean_t done = B_FALSE;
1989 char *return_buffer;
1991 /* A MIME message is a rather specialized beast. It consists of
1992 a series of headers describing the mail message, followed by
1993 the message, and then followed by a set of attachments.
1994 Each attachments is separated by a magic unique string
1995 flagging the boundaries between the attached objects. The
1996 first header is the address list. The second header is the
1997 subject line for the message. The third header describes the
1998 MIME version of the message. The forth describes the type of
1999 information within the message and the magic string used for
2002 Each division is two dashes, the unique string, and a newline
2003 The last dividing string is trailed by two more dashes. */
2006 /* we need to generate a unique dividing string that will
2007 not be found in any of the parts of the MIME message.
2008 The following printf will be tried until it generates
2009 something that isn't in any of the body parts. */
2011 while (done != B_TRUE) {
2012 sprintf(divider_string, "%x_%x-%x_%x-%x_%x", rand(), rand(),
2013 rand(), rand(), rand(), rand());
2017 for (i = 0; i < num_objects; i++) {
2018 if (strstr(appointment_objects[i], divider_string) != NULL)
2023 buffer_size = strlen(address_header) +
2024 strlen(address_list) +
2026 strlen(subject_header) +
2029 strlen(content_label) + 2 +
2030 (num_objects + 2) * strlen(divider_string) +
2031 /* one definition copy,
2032 one terminating copy,
2036 5 + strlen(divider_string) + /* empty body part */
2038 num_objects * strlen(x_content_name) +
2039 /* one X-Content-Name
2043 (2 * num_objects) + 4 + /* bracketing on unique
2045 (num_objects * 3) + 2; /* newlines...3 per
2048 terminating boundary */
2050 for (i = 0; i < num_objects; i++)
2051 buffer_size += strlen(appointment_objects[i]);
2053 /* extra byte is added for null char */
2054 return_buffer = (char *)calloc(buffer_size + 1, 1);
2056 sprintf(return_buffer, "%s%s\n%s%s\n%s%s\n\n",
2065 * Add an empty body part. This is a hack to get dtmail to
2066 * display the object(s) as an attachment.
2068 strcat(return_buffer, "\n--");
2069 strcat(return_buffer, divider_string);
2070 strcat(return_buffer, "\n\n");
2072 for (i = 0; i <num_objects; i++) {
2073 strcat(return_buffer, "\n--");
2074 strcat(return_buffer, divider_string);
2075 strcat(return_buffer, "\n");
2076 strcat(return_buffer, x_content_name);
2077 strcat(return_buffer, "\n");
2078 strcat(return_buffer, appointment_objects[i]);
2080 strcat(return_buffer, "\n--");
2081 strcat(return_buffer, divider_string);
2082 strcat(return_buffer, "--");
2083 strcat(return_buffer, "\n");
2085 return(return_buffer);
2089 appointments_to_file(CSA_session_handle target, CSA_entry_handle *appointment_list,
2096 FILE *f_ptr = fopen(file_name, "w");
2101 if (num_appts == 0) {
2106 fprintf(f_ptr, "DTCM Archive 1.0\n");
2107 for (i = 0; i < num_appts; i++) {
2108 entry_string = entry_to_attrval_string(target, appointment_list[i]);
2110 fprintf(f_ptr, "\n-%s:string:begin\n%s%s:string:end\n\n",
2111 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
2113 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER);
2124 * NOTE!! These are strings used in versions 1-4 - the repeat strings read
2125 * from a file or passed to the cm_tty_insert routine are checked against
2126 * these strings as well as the V5 API strings.
2128 static char *periodstrings[] = {
2135 "Monthly By Weekday",
2140 "Monday thru Friday",
2142 "Tuesday, Thursday",
2148 str_to_period(char *ps, CSA_sint32 *repeat_type, int *repeat_nth) {
2149 boolean_t compute_times = B_FALSE;
2150 char *ps2, *ptr, *ptr2, *unit;
2152 *repeat_type = '\0';
2157 if (strcasecmp(ps, periodstrings[0]) == 0)
2158 *repeat_type = CSA_X_DT_REPEAT_ONETIME;
2159 else if (strcasecmp(ps, periodstrings[1]) == 0)
2160 *repeat_type = CSA_X_DT_REPEAT_DAILY;
2161 else if (strcasecmp(ps, periodstrings[2]) == 0)
2162 *repeat_type = CSA_X_DT_REPEAT_WEEKLY;
2163 else if (strcasecmp(ps, periodstrings[3]) == 0)
2164 *repeat_type = CSA_X_DT_REPEAT_BIWEEKLY;
2165 else if (strcasecmp(ps, periodstrings[4]) == 0)
2166 *repeat_type = CSA_X_DT_REPEAT_MONTHLY_BY_DATE;
2167 else if (strcasecmp(ps, periodstrings[5]) == 0)
2168 *repeat_type = CSA_X_DT_REPEAT_YEARLY;
2169 else if (strncasecmp(ps, periodstrings[6],
2170 strlen(periodstrings[6])) == 0)
2171 *repeat_type = CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY;
2172 else if (strcasecmp(ps, periodstrings[10]) == 0)
2173 *repeat_type = CSA_X_DT_REPEAT_OTHER;
2174 else if (strcasecmp(ps, periodstrings[11]) == 0)
2175 *repeat_type = CSA_X_DT_REPEAT_MON_TO_FRI;
2176 else if (strcasecmp(ps, periodstrings[12]) == 0)
2177 *repeat_type = CSA_X_DT_REPEAT_MONWEDFRI;
2178 else if (strcasecmp(ps, periodstrings[13]) == 0)
2179 *repeat_type = CSA_X_DT_REPEAT_TUETHUR;
2180 else if (strcasecmp(ps, periodstrings[14]) == 0)
2181 *repeat_type = CSA_X_DT_REPEAT_WEEKDAYCOMBO;
2182 else if (strncasecmp(ps, periodstrings[15], strlen(periodstrings[15])) == 0) {
2183 compute_times = B_TRUE;
2186 *repeat_type = CSA_X_DT_REPEAT_ONETIME;
2188 if ((compute_times) && (unit = strchr(ps, ' '))) {
2189 while (*unit == ' ')
2191 ps2 = cm_strdup(unit);
2192 ptr = strchr(ps2, ' ');
2204 *repeat_nth = atoi(ps2);
2205 if (strcasecmp(ptr, periodstrings[7]) == 0) {
2206 *repeat_type = CSA_X_DT_REPEAT_EVERY_NDAY;
2208 else if (strcasecmp(ptr, periodstrings[8]) == 0) {
2209 *repeat_type = CSA_X_DT_REPEAT_EVERY_NWEEK;
2211 else if (strcasecmp(ptr, periodstrings[9]) == 0) {
2212 *repeat_type = CSA_X_DT_REPEAT_EVERY_NMONTH;
2219 periodstr_from_period(CSA_sint32 repeat_type, int repeat_nth) {
2220 static char pstr[80];
2222 switch (repeat_type) {
2223 case CSA_X_DT_REPEAT_ONETIME:
2224 sprintf(pstr, "%s", periodstrings[0]);
2226 case CSA_X_DT_REPEAT_DAILY:
2227 sprintf(pstr, "%s", periodstrings[1]);
2229 case CSA_X_DT_REPEAT_WEEKLY:
2230 sprintf(pstr, "%s", periodstrings[2]);
2232 case CSA_X_DT_REPEAT_BIWEEKLY:
2233 sprintf(pstr, "%s", periodstrings[3]);
2235 case CSA_X_DT_REPEAT_MONTHLY_BY_DATE:
2236 sprintf(pstr, "%s", periodstrings[4]);
2238 case CSA_X_DT_REPEAT_YEARLY:
2239 sprintf(pstr, "%s", periodstrings[5]);
2241 case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY:
2242 sprintf(pstr, "%s", periodstrings[6]);
2244 case CSA_X_DT_REPEAT_EVERY_NDAY:
2245 sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[7]);
2247 case CSA_X_DT_REPEAT_EVERY_NWEEK:
2248 sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[8]);
2250 case CSA_X_DT_REPEAT_EVERY_NMONTH:
2251 sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[9]);
2253 case CSA_X_DT_REPEAT_OTHER:
2254 sprintf(pstr, "%s", periodstrings[10]);
2256 case CSA_X_DT_REPEAT_MON_TO_FRI:
2257 sprintf(pstr, "%s", periodstrings[11]);
2259 case CSA_X_DT_REPEAT_MONWEDFRI:
2260 sprintf(pstr, "%s", periodstrings[12]);
2262 case CSA_X_DT_REPEAT_TUETHUR:
2263 sprintf(pstr, "%s", periodstrings[13]);
2265 case CSA_X_DT_REPEAT_WEEKDAYCOMBO:
2266 sprintf(pstr, "%s", periodstrings[14]);
2269 sprintf(pstr, "Unknown repeat type");
2277 * NOTE!! These first set of these strings are used in versions 1-4 - the
2278 * privacy strings read from a file or passed to the cm_tty_insert routine are
2279 * checked against these strings as well as the V5 API strings and the new
2282 static char *privacy_strs_old[] = {
2283 "Show Time And Text",
2288 static char *privacy_strs[] = {
2289 "Others See Time And Text",
2290 "Others See Time Only",
2291 "Others See Nothing"
2294 static char *privacy_strs_411[] = {
2301 privacy_str_old(int op) {
2302 if (op >= 0 && op <= 2)
2303 return privacy_strs_old[op];
2308 privacy_str(int op) {
2309 if (op >= 0 && op <= 2)
2310 return privacy_strs[op];
2319 repeat_strs[ONE_TIME] = strdup(catgets(catd, 1, 852, "One Time"));
2320 repeat_strs[DAILY] = strdup(catgets(catd, 1, 853, "Daily"));
2321 repeat_strs[WEEKLY] = strdup(catgets(catd, 1, 854, "Weekly"));
2322 repeat_strs[EVERY_TWO_WEEKS] =
2323 strdup(catgets(catd, 1, 855, "Every Two Weeks"));
2324 repeat_strs[MONTHLY_BY_DATE] =
2325 strdup(catgets(catd, 1, 856, "Monthly By Date"));
2326 repeat_strs[MONTHLY_BY_WEEKDAY] =
2327 strdup(catgets(catd, 1, 857, "Monthly By Weekday"));
2328 repeat_strs[YEARLY] =
2329 strdup(catgets(catd, 1, 858, "Yearly"));
2330 repeat_strs[MONDAY_THRU_FRIDAY] =
2331 strdup(catgets(catd, 1, 859, "Monday Thru Friday"));
2332 repeat_strs[MON_WED_FRI] =
2333 strdup(catgets(catd, 1, 860, "Mon, Wed, Fri"));
2334 repeat_strs[TUESDAY_THURSDAY] =
2335 strdup(catgets(catd, 1, 861, "Tuesday, Thursday"));
2336 repeat_strs[REPEAT_EVERY] =
2337 strdup(catgets(catd, 1, 862, "Repeat Every..."));
2345 if (!repeat_strs[0])
2346 init_repeat_strs(catd, repeat_strs);
2348 if (op >= ONE_TIME && op <= REPEAT_EVERY)
2349 return repeat_strs[op];
2354 privacy_str_411(int op) {
2355 if (op >= 0 && op <= 2)
2356 return privacy_strs_411[op];
2363 Repeat_scope_menu_op op)
2365 if (!repeat_scope_strs[0]) {
2366 repeat_scope_strs[REPEAT_DAYS] =
2367 strdup(catgets(catd, 1, 994, "days"));
2368 repeat_scope_strs[REPEAT_WEEKS] =
2369 strdup(catgets(catd, 1, 995, "weeks"));
2370 repeat_scope_strs[REPEAT_MONTHS] =
2371 strdup(catgets(catd, 1, 997, "months"));
2374 if (op >= REPEAT_DAYS && op <= REPEAT_MONTHS)
2375 return repeat_scope_strs[op];
2380 separator_str(SeparatorType op) {
2381 if (op >= SEPARATOR_BLANK && op <= SEPARATOR_DASH)
2382 return separator_strs[op];
2387 timescopestring_to_tick(char *str) {
2388 if (strcasecmp(time_scope_strs[1], str) == 0)
2390 else if (strcasecmp(time_scope_strs[2], str) == 0)
2396 time_scope_str(Time_scope_menu_op op) {
2397 if (op >= TIME_MINS && op <= TIME_DAYS)
2398 return time_scope_strs[op];
2403 time_scope_str_i18n(
2405 Time_scope_menu_op op)
2407 if (!time_scope_strs_i18n[0]) {
2408 time_scope_strs_i18n[TIME_MINS] =
2409 strdup(catgets(catd, 1, 877, "Mins"));
2410 time_scope_strs_i18n[TIME_HRS] =
2411 strdup(catgets(catd, 1, 878, "Hrs"));
2412 time_scope_strs_i18n[TIME_DAYS] =
2413 strdup(catgets(catd, 1, 879, "Days"));
2416 if (op >= TIME_MINS && op <= TIME_DAYS)
2417 return time_scope_strs_i18n[op];
2422 ** Determine whether or not the time passed is a valid format
2425 valid_time(Props *p, char *time_str) {
2427 int num_minutes = 0, num_colons = 0;
2428 boolean_t after_colon = B_FALSE;
2429 DisplayType dt = get_int_prop(p, CP_DEFAULTDISP);
2431 for (ptr = time_str; ptr != NULL && *ptr != '\0'; ptr++) {
2434 after_colon = B_TRUE;
2435 if ((++num_colons) > 1)
2437 if (*(ptr+1) == '\0')
2440 else if (*ptr != ' ' && (*ptr < '0' || *ptr > '9') )
2442 if ((after_colon) && (*ptr != ':'))
2444 if (num_minutes > 2)
2446 else if (num_minutes == 2) {
2448 if (strncasecmp(ptr, "am", 2) == 0 ||
2449 strncasecmp(ptr, "pm", 2) == 0)
2454 else if (dt == HOUR24) {
2455 if (*ptr != ' ' && (*ptr < '0' || *ptr > '9') )
2457 if (++num_minutes > 4)
2461 if (dt == HOUR12 && ((int)atoi(time_str) > 12))
2463 else if ((dt == HOUR24)
2464 && ((int)atoi(time_str) > 2359))
2471 * This method will validate the passed appointment data.
2474 * Date string incorrect INVALID_DATE
2475 * Tick for start is < 0 INVALID_START
2476 * Tick for stop is < 0 INVALID_STOP
2477 * Blank date MISSING_DATE
2478 * End time but no start time MISSING_START
2479 * Blank what with no times MISSING_WHAT
2480 * Period = single & for > 0 REPEAT_FOR_MISMATCH
2481 * Period != single & for = 0 REPEAT_FOR_MISMATCH
2483 * Note the function pointer passed to this function - if the end time is
2484 * before the start time, this function will be executed and should return
2485 * B_TRUE if the appointment should be scheduled to the next day, B_FALSE if
2486 * it should be canceled.
2489 validate_appt(nl_catd catd, Dtcm_appointment *a, char *s_buf, char *e_buf,
2490 char *d_buf, int dur, char *w_buf, char *r_buf, char *f_buf,
2491 boolean_t(*query)(void*), void *key_data, int version) {
2494 if ((op = validate_dssw(a, s_buf, e_buf, d_buf, dur, w_buf, query,
2495 key_data)) != VALID_APPT)
2497 if ((op = validate_rfp(catd, a, r_buf, f_buf, version)) != VALID_APPT)
2499 if ((op = validate_reminders(a)) != VALID_APPT)
2506 validate_dssw(Dtcm_appointment *a, char *s_buf, char *e_buf, char *d_buf,
2507 int dur, char *w_buf, boolean_t(*query)(), void *key_data) {
2509 char buf[MAXNAMELEN];
2512 if (blank_buf(d_buf))
2513 return MISSING_DATE;
2515 a->time->value->item.date_time_value = malloc(BUFSIZ);
2516 _csa_tick_to_iso8601(0, a->time->value->item.date_time_value);
2518 a->show_time->value->item.sint32_value = B_TRUE;
2520 if (w_buf && w_buf[0] != '\0') {
2521 a->what->value->item.string_value = cm_strdup(w_buf);
2522 expand_esc_chars(a->what->value->item.string_value);
2524 a->what->value->item.string_value = NULL;
2526 if (!blank_buf(s_buf)) {
2528 * We have something in the start buffer, is it valid?
2530 sprintf(buf, "%s %s", d_buf, s_buf);
2531 if ((appt_time = cm_getdate(buf, NULL)) < 0)
2532 return INVALID_START;
2534 _csa_tick_to_iso8601(appt_time, a->time->value->item.date_time_value);
2536 * Okay, we have a valid start time - do we have a duration
2541 * Duration is specified - add duration to start time
2543 end_tick = appt_time + dur;
2544 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2545 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2546 } else if (!blank_buf(e_buf)) {
2548 * No duration, but something in the end buffer. If
2549 * it's valid set the end tick to it's value.
2551 sprintf(buf, "%s %s", d_buf, e_buf);
2552 if ((end_tick = cm_getdate(buf, NULL)) < 0)
2553 return INVALID_STOP;
2554 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2555 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2558 * No duration or end buffer - set end_tick to starting
2559 * tick and duration to 0
2561 a->end_time->value->item.date_time_value =
2562 strdup(a->time->value->item.date_time_value);
2564 } else if (dur > 0 || !blank_buf(e_buf))
2565 return MISSING_START;
2567 if (blank_buf(a->what->value->item.string_value))
2568 return MISSING_WHAT;
2571 * If we're here, there was a date with no start or stop time,
2572 * so set time to magic time (3:41 am - don't ask where that
2573 * came from, 'cause I certainly don't know) and make sure the
2574 * date was correct. If so, set duration to 1 minute and
2575 * showtime to false.
2577 sprintf(buf, "%s 3:41am", d_buf);
2579 if ((appt_time = cm_getdate(buf, NULL)) < 0)
2580 return INVALID_DATE;
2582 a->time->value->item.date_time_value = malloc(BUFSIZ);
2583 _csa_tick_to_iso8601(appt_time, a->time->value->item.date_time_value);
2585 end_tick = appt_time + minsec;
2587 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2588 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2590 a->show_time->value->item.sint32_value = B_FALSE;
2594 * Finally, if the ending tick is before the starting tick, execute the
2595 * passed function which should return B_TRUE if we should schedule
2596 * this into the next day and B_FALSE if not.
2598 * This allows for methods calling this function to be UI oriented or
2599 * command line oriented as they can "query" the user appropriately.
2601 if (end_tick < appt_time) {
2602 if ((*query)(key_data) == B_TRUE) {
2603 while (end_tick < appt_time)
2606 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2617 validate_reminders(Dtcm_appointment *a) {
2624 Dtcm_appointment *a,
2632 repeat_forever = False;
2633 CSA_sint32 repeat_type = 0;
2634 CSA_uint32 repeat_times = 0;
2638 str_to_period(r_buf, &repeat_type, &repeat_nth);
2639 if (repeat_type == CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY) {
2640 r_buf += strlen(periodstrings[6]);
2641 while(*r_buf != '\0' && !isspace((u_char)*r_buf))
2643 while(*r_buf != '\0' && isspace((u_char)*r_buf))
2646 if (strncasecmp(r_buf, "last", 4) != 0)
2647 repeat_wk = atoi(r_buf);
2652 /* Repeat forever is represented by either:
2653 f_buf == ``forever'' or
2655 If it is a CSA_X_DT_REPEAT_ONETIME then
2658 if (strcasecmp(f_buf, catgets(catd, 1, 876, "forever")) == 0) {
2659 repeat_times = CSA_X_DT_DT_REPEAT_FOREVER;
2660 repeat_forever = True;
2662 repeat_times = atoi(f_buf);
2663 if (repeat_times == CSA_X_DT_DT_REPEAT_FOREVER &&
2664 repeat_type != CSA_X_DT_REPEAT_ONETIME)
2665 repeat_forever = True;
2669 /* If it is a onetime event then it cannot repeat.
2670 * If it is a repeating event then repeat_times must be greater
2671 * than 0 unless it is supposed to repeat forever.
2673 if (((repeat_type != CSA_X_DT_REPEAT_ONETIME) &&
2674 (repeat_times == 0) && (repeat_forever != True)) ||
2675 ((repeat_type == CSA_X_DT_REPEAT_ONETIME) &&
2676 ((repeat_times != 0) || (repeat_forever == True))))
2677 return REPEAT_FOR_MISMATCH;
2679 if (a->repeat_type && a->repeat_type->value)
2680 a->repeat_type->value->item.sint32_value = repeat_type;
2681 if (a->repeat_times && a->repeat_times->value)
2682 a->repeat_times->value->item.uint32_value = repeat_times;
2683 if (a->repeat_interval && a->repeat_interval->value)
2684 a->repeat_interval->value->item.uint32_value = repeat_nth;
2685 if (a->repeat_week_num && a->repeat_week_num->value)
2686 a->repeat_week_num->value->item.sint32_value = repeat_wk;
2688 /* If we are less than data version 4, we're done. */
2689 if (version < DATAVER4)
2692 /* Data version 4 appts use a recurrence rule. */
2693 memset (rule_buf, 0, 32);
2695 switch(repeat_type) {
2696 case CSA_X_DT_REPEAT_ONETIME:
2697 case CSA_X_DT_REPEAT_DAILY:
2698 strcpy(rule_buf, "D1 ");
2700 case CSA_X_DT_REPEAT_WEEKLY:
2701 strcpy(rule_buf, "W1 ");
2703 case CSA_X_DT_REPEAT_BIWEEKLY:
2704 strcpy(rule_buf, "W2 ");
2706 case CSA_X_DT_REPEAT_MONTHLY_BY_DATE:
2707 strcpy(rule_buf, "MD1 ");
2709 case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY: {
2713 if (a && a->time && a->time->value) {
2714 _csa_iso8601_to_tick(
2715 a->time->value->item.date_time_value,
2720 * The current behavior of cm/dtcm is that if an appt is
2721 * scheduled for the 5 wk of the month, it repeats on the
2722 * last week of the month.
2724 if (tick && weekofmonth(tick, &wk) && wk == 5)
2725 sprintf(rule_buf, "MP1 1- %s ", dow_str(tick));
2727 strcpy(rule_buf, "MP1 ");
2730 case CSA_X_DT_REPEAT_YEARLY:
2731 strcpy(rule_buf, "YM1 ");
2733 case CSA_X_DT_REPEAT_MON_TO_FRI:
2734 strcpy(rule_buf, "W1 MO TU WE TH FR ");
2736 case CSA_X_DT_REPEAT_MONWEDFRI:
2737 strcpy(rule_buf, "W1 MO WE FR ");
2739 case CSA_X_DT_REPEAT_TUETHUR:
2740 strcpy(rule_buf, "W1 TU TH ");
2742 case CSA_X_DT_REPEAT_EVERY_NDAY:
2743 sprintf(rule_buf, "D%d ", repeat_nth);
2744 repeat_every = True;
2746 case CSA_X_DT_REPEAT_EVERY_NWEEK:
2747 sprintf(rule_buf, "W%d ", repeat_nth);
2748 repeat_every = True;
2750 case CSA_X_DT_REPEAT_EVERY_NMONTH:
2751 sprintf(rule_buf, "MD%d ", repeat_nth);
2752 repeat_every = True;
2758 /* If the for buffer is NULL then we default to repeating one time.
2759 * If the repeat_type is onetime then we default repeat times to
2762 if (!f_buf || repeat_type == CSA_X_DT_REPEAT_ONETIME)
2765 if (repeat_times == CSA_X_DT_DT_REPEAT_FOREVER) {
2766 strcat(rule_buf, "#0");
2773 if (repeat_times % repeat_nth)
2774 duration = 1 + repeat_times/repeat_nth;
2776 duration = repeat_times/repeat_nth;
2777 sprintf(buf, "#%d", duration);
2779 sprintf(buf, "#%ld", repeat_times);
2781 strcat(rule_buf, buf);
2783 a->recurrence_rule->value->item.string_value = strdup(rule_buf);