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;
1265 CSA_uint32 repeat_for;
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) {
1628 return(INVALID_DATE);
1631 if (processing_appt) {
1632 CmDataListAdd(list, (void *)appt, 0);
1633 valid_op = validate_appt(catd, appt, s_buf, e_buf, d_buf, dur,
1634 w_buf, r_buf, f_buf, query, key_data,
1641 scrub_attr_list(appt);
1648 growcat(char **source, char *new)
1650 *source = (char *) realloc(*source, strlen(*source) + strlen(new) + 2);
1651 strcat(*source, new);
1655 cat_indented_string(char **catbuf, char *string)
1657 char *p_str = string;
1662 nl_count = count_newlines(string);
1664 b_ptr = buf = malloc(strlen(string) + nl_count + 1);
1674 growcat(catbuf, buf);
1680 attrs_to_string(CSA_attribute * attrs, int num_attrs)
1683 CSA_access_list a_ptr;
1684 char *buffer = malloc(1);
1685 char tmp_buf[MAXNAMELEN];
1689 for (i = 0; i < num_attrs; i++) {
1690 if (!attrs[i].name || (attrs[i].value == NULL))
1694 sprintf(tmp_buf, "%s:", attrs[i].name);
1695 switch (attrs[i].value->type) {
1696 case CSA_VALUE_SINT32:
1697 growcat(&buffer, tmp_buf);
1698 sprintf(tmp_buf, "sinteger:%ld\n",
1699 attrs[i].value->item.sint32_value);
1700 growcat(&buffer, tmp_buf);
1703 case CSA_VALUE_UINT32:
1704 growcat(&buffer, tmp_buf);
1705 sprintf(tmp_buf, "uinteger:%ld\n",
1706 attrs[i].value->item.uint32_value);
1707 growcat(&buffer, tmp_buf);
1710 case CSA_VALUE_DATE_TIME:
1711 if (attrs[i].value->item.string_value == NULL)
1714 growcat(&buffer, tmp_buf);
1715 growcat(&buffer, "datetime:");
1716 if (attrs[i].value->item.date_time_value)
1717 cat_indented_string(&buffer,
1718 attrs[i].value->item.string_value);
1719 growcat(&buffer, "\n");
1722 case CSA_VALUE_STRING:
1723 if (attrs[i].value->item.string_value == NULL)
1726 growcat(&buffer, tmp_buf);
1727 growcat(&buffer, "string:");
1728 if (attrs[i].value->item.string_value)
1729 cat_indented_string(&buffer,
1730 attrs[i].value->item.string_value);
1731 growcat(&buffer, "\n");
1733 case CSA_VALUE_CALENDAR_USER:
1734 if (attrs[i].value->item.calendar_user_value == NULL)
1737 growcat(&buffer, tmp_buf);
1738 sprintf(tmp_buf, "caluser:%ld:", attrs[i].value->item.calendar_user_value->user_type);
1739 growcat(&buffer, tmp_buf);
1740 if (attrs[i].value->item.calendar_user_value->user_name)
1741 cat_indented_string(&buffer,
1742 attrs[i].value->item.calendar_user_value->user_name);
1744 growcat(&buffer, "\n");
1747 case CSA_VALUE_REMINDER:
1748 if (attrs[i].value->item.reminder_value->lead_time == NULL)
1751 growcat(&buffer, tmp_buf);
1752 _csa_iso8601_to_duration(attrs[i].value->item.reminder_value->lead_time, &advance_time);
1753 sprintf(tmp_buf, "reminder:%d:", advance_time);
1754 growcat(&buffer, tmp_buf);
1755 if (attrs[i].value->item.reminder_value->reminder_data.data)
1756 cat_indented_string(&buffer,
1757 (char *) attrs[i].value->item.reminder_value->reminder_data.data);
1758 growcat(&buffer, "\n");
1761 case CSA_VALUE_ACCESS_LIST:
1762 if (attrs[i].value->item.access_list_value == NULL)
1765 growcat(&buffer, tmp_buf);
1766 growcat(&buffer, "accesslist:\n");
1767 a_ptr = attrs[i].value->item.access_list_value;
1769 sprintf(tmp_buf, "\t%d:%s\n",
1770 (int) a_ptr->rights, a_ptr->user->user_name);
1771 growcat(&buffer, tmp_buf);
1772 a_ptr = a_ptr->next;
1782 entry_to_attrval_string(CSA_session_handle target, CSA_entry_handle entry)
1786 CSA_attribute_reference *names;
1787 CSA_attribute *attrs_ret;
1788 CSA_uint32 num_attrs, num_attrs_ret;
1790 csa_list_entry_attributes(target, entry, &num_attrs, &names, NULL);
1791 csa_read_entry_attributes(target, entry, num_attrs, names, &num_attrs_ret,
1794 ptr = attrs_to_string(attrs_ret, num_attrs_ret);
1797 ** Need to check that library's memory management will
1798 ** properly free this. Old scheme had a special fn to
1799 ** to it, which took a pointer and a count. - dac
1801 ** [old version: DtCmFreeAttributeValues(attrs, num_attrs); ]
1803 csa_free(attrs_ret);
1810 calendar_to_attrval_string(CSA_session_handle cal)
1813 CSA_uint32 num_attrs, num_attrs_ret;
1814 CSA_attribute_reference *names;
1816 CSA_attribute *attrs_ret;
1818 csa_list_calendar_attributes(cal, &num_attrs, &names, NULL);
1819 csa_read_calendar_attributes(cal, num_attrs, names, &num_attrs_ret,
1822 ptr = attrs_to_string(attrs_ret, num_attrs_ret);
1824 csa_free(attrs_ret);
1831 * Given an appointment structure, return a character string that can be used
1832 * for DnD or written to a file.
1835 parse_appt_to_string(CSA_session_handle target, CSA_entry_handle entry, Props *p, int version) {
1836 char *ret_val, *attr_string;
1837 Dtcm_appointment *appt;
1838 CSA_uint32 na_ret; /* num of attributes actually read */
1839 CSA_attribute a_ret; /* list of attrs actually read */
1841 attr_string = entry_to_attrval_string(target, entry);
1844 * Extract the info we need from the back-end entry
1846 appt = allocate_appt_struct(appt_read,
1850 query_appt_struct(target, entry, appt);
1851 ret_val = parse_attrs_to_string(appt, p, attr_string);
1852 free_appt_struct(&appt);
1859 parse_attrs_to_string(Dtcm_appointment *appt, Props *p, char *attr_string) {
1860 int nlcount, duration, repeat_nth, repeat_wk, wk;
1861 char *whatstr, d_buf[MAXNAMELEN],
1862 s_buf[MAXNAMELEN], e_buf[MAXNAMELEN], w_buf[MAXNAMELEN],
1863 r_buf[MAXNAMELEN], f_buf[MAXNAMELEN], *appt_what,
1865 time_t tick, end_tick = 0;
1866 CSA_sint32 repeat_type;
1867 CSA_uint32 repeat_times;
1868 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";
1876 _csa_iso8601_to_tick(appt->time->value->item.date_time_value, &tick);
1878 if (appt->end_time->value != NULL)
1879 _csa_iso8601_to_tick(appt->end_time->value->item.\
1880 date_time_value, &end_tick);
1882 appt_what = appt->what->value->item.string_value;
1884 if (appt->repeat_type && appt->repeat_type->value)
1885 repeat_type = appt->repeat_type->value->item.sint32_value;
1887 if (appt->repeat_times && appt->repeat_times->value)
1888 repeat_times = appt->repeat_times->value->item.uint32_value;
1892 if (appt->repeat_interval && appt->repeat_interval->value)
1893 repeat_nth = appt->repeat_interval->value->item.uint32_value;
1897 if (appt->repeat_week_num && appt->repeat_week_num->value)
1898 repeat_wk = appt->repeat_week_num->value->item.sint32_value;
1903 * Format the date and start/stop strings
1905 format_tick(tick, ORDER_MDY, SEPARATOR_SLASH, d_buf);
1906 format_time(tick, get_int_prop(p, CP_DEFAULTDISP), s_buf);
1907 format_time(end_tick, get_int_prop(p, CP_DEFAULTDISP), e_buf);
1910 * Handle the what string
1912 whatstr = appt_what;
1913 if ((nlcount = count_newlines(appt_what)) > 0) {
1914 whatstr = (char *)ckalloc(cm_strlen(appt_what) + nlcount + 1);
1915 copy_and_pad_newlines(whatstr, appt_what);
1917 if (whatstr && !blank_buf(whatstr)) {
1918 cm_strcat(w_buf, whatstr);
1919 cm_strcat(w_buf, "\n\t");
1923 * Repeat and For stuff
1925 cm_strcpy(r_buf, periodstr_from_period(repeat_type, repeat_nth));
1926 if (repeat_type == CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY) {
1927 if (weekofmonth(tick, &wk) && wk == 4) {
1928 if (repeat_wk == -1)
1929 strcat(r_buf, ", last");
1930 else if (repeat_wk == 4)
1931 strcat(r_buf, ", 4th");
1935 sprintf(f_buf, "%ld", repeat_times);
1938 * Put it all together
1941 b_ptr = malloc(cm_strlen(d_buf) +
1942 (2 * cm_strlen(CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER)) +
1948 cm_strlen(format_string) +
1949 cm_strlen(attr_string) +
1952 sprintf(b_ptr, format_string,
1953 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
1955 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
1956 d_buf, s_buf, e_buf, r_buf, f_buf, w_buf);
1963 /* This routine takes a list of buffers that represent a number
1964 of message attachments, and glues them into a mime format
1965 message for eventual submission to the dtmail mailer. These
1966 objects do nor *need* to be cm appointment objects. */
1969 create_rfc_message(char *address_list,
1971 char **appointment_objects,
1976 /* do *not* put these header strings in a message catalog.
1977 These are invariants specified by MIME */
1979 char *address_header = "To: ";
1980 char *subject_header = "Subject: ";
1981 char *x_content_name = "X-Content-Name: CalendarAppointment\n";
1982 char *content_label = "Mime-Version: 1.0\nContent-Type: multipart/mixed;boundary=";
1983 char divider_string[32];
1987 boolean_t done = B_FALSE;
1988 char *return_buffer;
1990 /* A MIME message is a rather specialized beast. It consists of
1991 a series of headers describing the mail message, followed by
1992 the message, and then followed by a set of attachments.
1993 Each attachments is separated by a magic unique string
1994 flagging the boundaries between the attached objects. The
1995 first header is the address list. The second header is the
1996 subject line for the message. The third header describes the
1997 MIME version of the message. The forth describes the type of
1998 information within the message and the magic string used for
2001 Each division is two dashes, the unique string, and a newline
2002 The last dividing string is trailed by two more dashes. */
2005 /* we need to generate a unique dividing string that will
2006 not be found in any of the parts of the MIME message.
2007 The following printf will be tried until it generates
2008 something that isn't in any of the body parts. */
2010 while (done != B_TRUE) {
2011 sprintf(divider_string, "%x_%x-%x_%x-%x_%x", rand(), rand(),
2012 rand(), rand(), rand(), rand());
2016 for (i = 0; i < num_objects; i++) {
2017 if (strstr(appointment_objects[i], divider_string) != NULL)
2022 buffer_size = strlen(address_header) +
2023 strlen(address_list) +
2025 strlen(subject_header) +
2028 strlen(content_label) + 2 +
2029 (num_objects + 2) * strlen(divider_string) +
2030 /* one definition copy,
2031 one terminating copy,
2035 5 + strlen(divider_string) + /* empty body part */
2037 num_objects * strlen(x_content_name) +
2038 /* one X-Content-Name
2042 (2 * num_objects) + 4 + /* bracketing on unique
2044 (num_objects * 3) + 2; /* newlines...3 per
2047 terminating boundary */
2049 for (i = 0; i < num_objects; i++)
2050 buffer_size += strlen(appointment_objects[i]);
2052 /* extra byte is added for null char */
2053 return_buffer = (char *)calloc(buffer_size + 1, 1);
2055 sprintf(return_buffer, "%s%s\n%s%s\n%s%s\n\n",
2064 * Add an empty body part. This is a hack to get dtmail to
2065 * display the object(s) as an attachment.
2067 strcat(return_buffer, "\n--");
2068 strcat(return_buffer, divider_string);
2069 strcat(return_buffer, "\n\n");
2071 for (i = 0; i <num_objects; i++) {
2072 strcat(return_buffer, "\n--");
2073 strcat(return_buffer, divider_string);
2074 strcat(return_buffer, "\n");
2075 strcat(return_buffer, x_content_name);
2076 strcat(return_buffer, "\n");
2077 strcat(return_buffer, appointment_objects[i]);
2079 strcat(return_buffer, "\n--");
2080 strcat(return_buffer, divider_string);
2081 strcat(return_buffer, "--");
2082 strcat(return_buffer, "\n");
2084 return(return_buffer);
2088 appointments_to_file(CSA_session_handle target, CSA_entry_handle *appointment_list,
2095 FILE *f_ptr = fopen(file_name, "w");
2100 if (num_appts == 0) {
2105 fprintf(f_ptr, "DTCM Archive 1.0\n");
2106 for (i = 0; i < num_appts; i++) {
2107 entry_string = entry_to_attrval_string(target, appointment_list[i]);
2109 fprintf(f_ptr, "\n-%s:string:begin\n%s%s:string:end\n\n",
2110 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
2112 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER);
2123 * NOTE!! These are strings used in versions 1-4 - the repeat strings read
2124 * from a file or passed to the cm_tty_insert routine are checked against
2125 * these strings as well as the V5 API strings.
2127 static char *periodstrings[] = {
2134 "Monthly By Weekday",
2139 "Monday thru Friday",
2141 "Tuesday, Thursday",
2147 str_to_period(char *ps, CSA_sint32 *repeat_type, int *repeat_nth) {
2148 boolean_t compute_times = B_FALSE;
2149 char *ps2, *ptr, *ptr2, *unit;
2151 *repeat_type = '\0';
2156 if (strcasecmp(ps, periodstrings[0]) == 0)
2157 *repeat_type = CSA_X_DT_REPEAT_ONETIME;
2158 else if (strcasecmp(ps, periodstrings[1]) == 0)
2159 *repeat_type = CSA_X_DT_REPEAT_DAILY;
2160 else if (strcasecmp(ps, periodstrings[2]) == 0)
2161 *repeat_type = CSA_X_DT_REPEAT_WEEKLY;
2162 else if (strcasecmp(ps, periodstrings[3]) == 0)
2163 *repeat_type = CSA_X_DT_REPEAT_BIWEEKLY;
2164 else if (strcasecmp(ps, periodstrings[4]) == 0)
2165 *repeat_type = CSA_X_DT_REPEAT_MONTHLY_BY_DATE;
2166 else if (strcasecmp(ps, periodstrings[5]) == 0)
2167 *repeat_type = CSA_X_DT_REPEAT_YEARLY;
2168 else if (strncasecmp(ps, periodstrings[6],
2169 strlen(periodstrings[6])) == 0)
2170 *repeat_type = CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY;
2171 else if (strcasecmp(ps, periodstrings[10]) == 0)
2172 *repeat_type = CSA_X_DT_REPEAT_OTHER;
2173 else if (strcasecmp(ps, periodstrings[11]) == 0)
2174 *repeat_type = CSA_X_DT_REPEAT_MON_TO_FRI;
2175 else if (strcasecmp(ps, periodstrings[12]) == 0)
2176 *repeat_type = CSA_X_DT_REPEAT_MONWEDFRI;
2177 else if (strcasecmp(ps, periodstrings[13]) == 0)
2178 *repeat_type = CSA_X_DT_REPEAT_TUETHUR;
2179 else if (strcasecmp(ps, periodstrings[14]) == 0)
2180 *repeat_type = CSA_X_DT_REPEAT_WEEKDAYCOMBO;
2181 else if (strncasecmp(ps, periodstrings[15], strlen(periodstrings[15])) == 0) {
2182 compute_times = B_TRUE;
2185 *repeat_type = CSA_X_DT_REPEAT_ONETIME;
2187 if ((compute_times) && (unit = strchr(ps, ' '))) {
2188 while (*unit == ' ')
2190 ps2 = cm_strdup(unit);
2191 ptr = strchr(ps2, ' ');
2203 *repeat_nth = atoi(ps2);
2204 if (strcasecmp(ptr, periodstrings[7]) == 0) {
2205 *repeat_type = CSA_X_DT_REPEAT_EVERY_NDAY;
2207 else if (strcasecmp(ptr, periodstrings[8]) == 0) {
2208 *repeat_type = CSA_X_DT_REPEAT_EVERY_NWEEK;
2210 else if (strcasecmp(ptr, periodstrings[9]) == 0) {
2211 *repeat_type = CSA_X_DT_REPEAT_EVERY_NMONTH;
2218 periodstr_from_period(CSA_sint32 repeat_type, int repeat_nth) {
2219 static char pstr[80];
2221 switch (repeat_type) {
2222 case CSA_X_DT_REPEAT_ONETIME:
2223 sprintf(pstr, "%s", periodstrings[0]);
2225 case CSA_X_DT_REPEAT_DAILY:
2226 sprintf(pstr, "%s", periodstrings[1]);
2228 case CSA_X_DT_REPEAT_WEEKLY:
2229 sprintf(pstr, "%s", periodstrings[2]);
2231 case CSA_X_DT_REPEAT_BIWEEKLY:
2232 sprintf(pstr, "%s", periodstrings[3]);
2234 case CSA_X_DT_REPEAT_MONTHLY_BY_DATE:
2235 sprintf(pstr, "%s", periodstrings[4]);
2237 case CSA_X_DT_REPEAT_YEARLY:
2238 sprintf(pstr, "%s", periodstrings[5]);
2240 case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY:
2241 sprintf(pstr, "%s", periodstrings[6]);
2243 case CSA_X_DT_REPEAT_EVERY_NDAY:
2244 sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[7]);
2246 case CSA_X_DT_REPEAT_EVERY_NWEEK:
2247 sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[8]);
2249 case CSA_X_DT_REPEAT_EVERY_NMONTH:
2250 sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[9]);
2252 case CSA_X_DT_REPEAT_OTHER:
2253 sprintf(pstr, "%s", periodstrings[10]);
2255 case CSA_X_DT_REPEAT_MON_TO_FRI:
2256 sprintf(pstr, "%s", periodstrings[11]);
2258 case CSA_X_DT_REPEAT_MONWEDFRI:
2259 sprintf(pstr, "%s", periodstrings[12]);
2261 case CSA_X_DT_REPEAT_TUETHUR:
2262 sprintf(pstr, "%s", periodstrings[13]);
2264 case CSA_X_DT_REPEAT_WEEKDAYCOMBO:
2265 sprintf(pstr, "%s", periodstrings[14]);
2268 sprintf(pstr, "Unknown repeat type");
2276 * NOTE!! These first set of these strings are used in versions 1-4 - the
2277 * privacy strings read from a file or passed to the cm_tty_insert routine are
2278 * checked against these strings as well as the V5 API strings and the new
2281 static char *privacy_strs_old[] = {
2282 "Show Time And Text",
2287 static char *privacy_strs[] = {
2288 "Others See Time And Text",
2289 "Others See Time Only",
2290 "Others See Nothing"
2293 static char *privacy_strs_411[] = {
2300 privacy_str_old(int op) {
2301 if (op >= 0 && op <= 2)
2302 return privacy_strs_old[op];
2307 privacy_str(int op) {
2308 if (op >= 0 && op <= 2)
2309 return privacy_strs[op];
2318 repeat_strs[ONE_TIME] = strdup(catgets(catd, 1, 852, "One Time"));
2319 repeat_strs[DAILY] = strdup(catgets(catd, 1, 853, "Daily"));
2320 repeat_strs[WEEKLY] = strdup(catgets(catd, 1, 854, "Weekly"));
2321 repeat_strs[EVERY_TWO_WEEKS] =
2322 strdup(catgets(catd, 1, 855, "Every Two Weeks"));
2323 repeat_strs[MONTHLY_BY_DATE] =
2324 strdup(catgets(catd, 1, 856, "Monthly By Date"));
2325 repeat_strs[MONTHLY_BY_WEEKDAY] =
2326 strdup(catgets(catd, 1, 857, "Monthly By Weekday"));
2327 repeat_strs[YEARLY] =
2328 strdup(catgets(catd, 1, 858, "Yearly"));
2329 repeat_strs[MONDAY_THRU_FRIDAY] =
2330 strdup(catgets(catd, 1, 859, "Monday Thru Friday"));
2331 repeat_strs[MON_WED_FRI] =
2332 strdup(catgets(catd, 1, 860, "Mon, Wed, Fri"));
2333 repeat_strs[TUESDAY_THURSDAY] =
2334 strdup(catgets(catd, 1, 861, "Tuesday, Thursday"));
2335 repeat_strs[REPEAT_EVERY] =
2336 strdup(catgets(catd, 1, 862, "Repeat Every..."));
2344 if (!repeat_strs[0])
2345 init_repeat_strs(catd, repeat_strs);
2347 if (op >= ONE_TIME && op <= REPEAT_EVERY)
2348 return repeat_strs[op];
2353 privacy_str_411(int op) {
2354 if (op >= 0 && op <= 2)
2355 return privacy_strs_411[op];
2362 Repeat_scope_menu_op op)
2364 if (!repeat_scope_strs[0]) {
2365 repeat_scope_strs[REPEAT_DAYS] =
2366 strdup(catgets(catd, 1, 994, "days"));
2367 repeat_scope_strs[REPEAT_WEEKS] =
2368 strdup(catgets(catd, 1, 995, "weeks"));
2369 repeat_scope_strs[REPEAT_MONTHS] =
2370 strdup(catgets(catd, 1, 997, "months"));
2373 if (op >= REPEAT_DAYS && op <= REPEAT_MONTHS)
2374 return repeat_scope_strs[op];
2379 separator_str(SeparatorType op) {
2380 if (op >= SEPARATOR_BLANK && op <= SEPARATOR_DASH)
2381 return separator_strs[op];
2386 timescopestring_to_tick(char *str) {
2387 if (strcasecmp(time_scope_strs[1], str) == 0)
2389 else if (strcasecmp(time_scope_strs[2], str) == 0)
2395 time_scope_str(Time_scope_menu_op op) {
2396 if (op >= TIME_MINS && op <= TIME_DAYS)
2397 return time_scope_strs[op];
2402 time_scope_str_i18n(
2404 Time_scope_menu_op op)
2406 if (!time_scope_strs_i18n[0]) {
2407 time_scope_strs_i18n[TIME_MINS] =
2408 strdup(catgets(catd, 1, 877, "Mins"));
2409 time_scope_strs_i18n[TIME_HRS] =
2410 strdup(catgets(catd, 1, 878, "Hrs"));
2411 time_scope_strs_i18n[TIME_DAYS] =
2412 strdup(catgets(catd, 1, 879, "Days"));
2415 if (op >= TIME_MINS && op <= TIME_DAYS)
2416 return time_scope_strs_i18n[op];
2421 ** Determine whether or not the time passed is a valid format
2424 valid_time(Props *p, char *time_str) {
2426 int num_minutes = 0, num_colons = 0;
2427 boolean_t after_colon = B_FALSE;
2428 DisplayType dt = get_int_prop(p, CP_DEFAULTDISP);
2430 for (ptr = time_str; ptr != NULL && *ptr != '\0'; ptr++) {
2433 after_colon = B_TRUE;
2434 if ((++num_colons) > 1)
2436 if (*(ptr+1) == '\0')
2439 else if (*ptr != ' ' && (*ptr < '0' || *ptr > '9') )
2441 if ((after_colon) && (*ptr != ':'))
2443 if (num_minutes > 2)
2445 else if (num_minutes == 2) {
2447 if (strncasecmp(ptr, "am", 2) == 0 ||
2448 strncasecmp(ptr, "pm", 2) == 0)
2453 else if (dt == HOUR24) {
2454 if (*ptr != ' ' && (*ptr < '0' || *ptr > '9') )
2456 if (++num_minutes > 4)
2460 if (dt == HOUR12 && ((int)atoi(time_str) > 12))
2462 else if ((dt == HOUR24)
2463 && ((int)atoi(time_str) > 2359))
2470 * This method will validate the passed appointment data.
2473 * Date string incorrect INVALID_DATE
2474 * Tick for start is < 0 INVALID_START
2475 * Tick for stop is < 0 INVALID_STOP
2476 * Blank date MISSING_DATE
2477 * End time but no start time MISSING_START
2478 * Blank what with no times MISSING_WHAT
2479 * Period = single & for > 0 REPEAT_FOR_MISMATCH
2480 * Period != single & for = 0 REPEAT_FOR_MISMATCH
2482 * Note the function pointer passed to this function - if the end time is
2483 * before the start time, this function will be executed and should return
2484 * B_TRUE if the appointment should be scheduled to the next day, B_FALSE if
2485 * it should be canceled.
2488 validate_appt(nl_catd catd, Dtcm_appointment *a, char *s_buf, char *e_buf,
2489 char *d_buf, int dur, char *w_buf, char *r_buf, char *f_buf,
2490 boolean_t(*query)(void*), void *key_data, int version) {
2493 if ((op = validate_dssw(a, s_buf, e_buf, d_buf, dur, w_buf, query,
2494 key_data)) != VALID_APPT)
2496 if ((op = validate_rfp(catd, a, r_buf, f_buf, version)) != VALID_APPT)
2498 if ((op = validate_reminders(a)) != VALID_APPT)
2505 validate_dssw(Dtcm_appointment *a, char *s_buf, char *e_buf, char *d_buf,
2506 int dur, char *w_buf, boolean_t(*query)(), void *key_data) {
2508 char buf[MAXNAMELEN];
2511 if (blank_buf(d_buf))
2512 return MISSING_DATE;
2514 a->time->value->item.date_time_value = malloc(BUFSIZ);
2515 _csa_tick_to_iso8601(0, a->time->value->item.date_time_value);
2517 a->show_time->value->item.sint32_value = B_TRUE;
2519 if (w_buf && w_buf[0] != '\0') {
2520 a->what->value->item.string_value = cm_strdup(w_buf);
2521 expand_esc_chars(a->what->value->item.string_value);
2523 a->what->value->item.string_value = NULL;
2525 if (!blank_buf(s_buf)) {
2527 * We have something in the start buffer, is it valid?
2529 sprintf(buf, "%s %s", d_buf, s_buf);
2530 if ((appt_time = cm_getdate(buf, NULL)) < 0)
2531 return INVALID_START;
2533 _csa_tick_to_iso8601(appt_time, a->time->value->item.date_time_value);
2535 * Okay, we have a valid start time - do we have a duration
2540 * Duration is specified - add duration to start time
2542 end_tick = appt_time + dur;
2543 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2544 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2545 } else if (!blank_buf(e_buf)) {
2547 * No duration, but something in the end buffer. If
2548 * it's valid set the end tick to it's value.
2550 sprintf(buf, "%s %s", d_buf, e_buf);
2551 if ((end_tick = cm_getdate(buf, NULL)) < 0)
2552 return INVALID_STOP;
2553 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2554 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2557 * No duration or end buffer - set end_tick to starting
2558 * tick and duration to 0
2560 a->end_time->value->item.date_time_value =
2561 strdup(a->time->value->item.date_time_value);
2563 } else if (dur > 0 || !blank_buf(e_buf))
2564 return MISSING_START;
2566 if (blank_buf(a->what->value->item.string_value))
2567 return MISSING_WHAT;
2570 * If we're here, there was a date with no start or stop time,
2571 * so set time to magic time (3:41 am - don't ask where that
2572 * came from, 'cause I certainly don't know) and make sure the
2573 * date was correct. If so, set duration to 1 minute and
2574 * showtime to false.
2576 sprintf(buf, "%s 3:41am", d_buf);
2578 if ((appt_time = cm_getdate(buf, NULL)) < 0)
2579 return INVALID_DATE;
2581 a->time->value->item.date_time_value = malloc(BUFSIZ);
2582 _csa_tick_to_iso8601(appt_time, a->time->value->item.date_time_value);
2584 end_tick = appt_time + minsec;
2586 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2587 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2589 a->show_time->value->item.sint32_value = B_FALSE;
2593 * Finally, if the ending tick is before the starting tick, execute the
2594 * passed function which should return B_TRUE if we should schedule
2595 * this into the next day and B_FALSE if not.
2597 * This allows for methods calling this function to be UI oriented or
2598 * command line oriented as they can "query" the user appropriately.
2600 if (end_tick < appt_time) {
2601 if ((*query)(key_data) == B_TRUE) {
2602 while (end_tick < appt_time)
2605 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2616 validate_reminders(Dtcm_appointment *a) {
2623 Dtcm_appointment *a,
2631 repeat_forever = False;
2632 CSA_sint32 repeat_type;
2633 CSA_uint32 repeat_times = 0;
2637 str_to_period(r_buf, &repeat_type, &repeat_nth);
2638 if (repeat_type == CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY) {
2639 r_buf += strlen(periodstrings[6]);
2640 while(*r_buf != '\0' && !isspace((u_char)*r_buf))
2642 while(*r_buf != '\0' && isspace((u_char)*r_buf))
2645 if (strncasecmp(r_buf, "last", 4) != 0)
2646 repeat_wk = atoi(r_buf);
2651 /* Repeat forever is represented by either:
2652 f_buf == ``forever'' or
2654 If it is a CSA_X_DT_REPEAT_ONETIME then
2657 if (strcasecmp(f_buf, catgets(catd, 1, 876, "forever")) == 0) {
2658 repeat_times = CSA_X_DT_DT_REPEAT_FOREVER;
2659 repeat_forever = True;
2661 repeat_times = atoi(f_buf);
2662 if (repeat_times == CSA_X_DT_DT_REPEAT_FOREVER &&
2663 repeat_type != CSA_X_DT_REPEAT_ONETIME)
2664 repeat_forever = True;
2668 /* If it is a onetime event then it cannot repeat.
2669 * If it is a repeating event then repeat_times must be greater
2670 * than 0 unless it is supposed to repeat forever.
2672 if (((repeat_type != CSA_X_DT_REPEAT_ONETIME) &&
2673 (repeat_times == 0) && (repeat_forever != True)) ||
2674 ((repeat_type == CSA_X_DT_REPEAT_ONETIME) &&
2675 ((repeat_times != 0) || (repeat_forever == True))))
2676 return REPEAT_FOR_MISMATCH;
2678 if (a->repeat_type && a->repeat_type->value)
2679 a->repeat_type->value->item.sint32_value = repeat_type;
2680 if (a->repeat_times && a->repeat_times->value)
2681 a->repeat_times->value->item.uint32_value = repeat_times;
2682 if (a->repeat_interval && a->repeat_interval->value)
2683 a->repeat_interval->value->item.uint32_value = repeat_nth;
2684 if (a->repeat_week_num && a->repeat_week_num->value)
2685 a->repeat_week_num->value->item.sint32_value = repeat_wk;
2687 /* If we are less than data version 4, we're done. */
2688 if (version < DATAVER4)
2691 /* Data version 4 appts use a recurrence rule. */
2692 memset (rule_buf, 0, 32);
2694 switch(repeat_type) {
2695 case CSA_X_DT_REPEAT_ONETIME:
2696 case CSA_X_DT_REPEAT_DAILY:
2697 strcpy(rule_buf, "D1 ");
2699 case CSA_X_DT_REPEAT_WEEKLY:
2700 strcpy(rule_buf, "W1 ");
2702 case CSA_X_DT_REPEAT_BIWEEKLY:
2703 strcpy(rule_buf, "W2 ");
2705 case CSA_X_DT_REPEAT_MONTHLY_BY_DATE:
2706 strcpy(rule_buf, "MD1 ");
2708 case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY: {
2712 if (a && a->time && a->time->value) {
2713 _csa_iso8601_to_tick(
2714 a->time->value->item.date_time_value,
2719 * The current behavior of cm/dtcm is that if an appt is
2720 * scheduled for the 5 wk of the month, it repeats on the
2721 * last week of the month.
2723 if (tick && weekofmonth(tick, &wk) && wk == 5)
2724 sprintf(rule_buf, "MP1 1- %s ", dow_str(tick));
2726 strcpy(rule_buf, "MP1 ");
2729 case CSA_X_DT_REPEAT_YEARLY:
2730 strcpy(rule_buf, "YM1 ");
2732 case CSA_X_DT_REPEAT_MON_TO_FRI:
2733 strcpy(rule_buf, "W1 MO TU WE TH FR ");
2735 case CSA_X_DT_REPEAT_MONWEDFRI:
2736 strcpy(rule_buf, "W1 MO WE FR ");
2738 case CSA_X_DT_REPEAT_TUETHUR:
2739 strcpy(rule_buf, "W1 TU TH ");
2741 case CSA_X_DT_REPEAT_EVERY_NDAY:
2742 sprintf(rule_buf, "D%d ", repeat_nth);
2743 repeat_every = True;
2745 case CSA_X_DT_REPEAT_EVERY_NWEEK:
2746 sprintf(rule_buf, "W%d ", repeat_nth);
2747 repeat_every = True;
2749 case CSA_X_DT_REPEAT_EVERY_NMONTH:
2750 sprintf(rule_buf, "MD%d ", repeat_nth);
2751 repeat_every = True;
2757 /* If the for buffer is NULL then we default to repeating one time.
2758 * If the repeat_type is onetime then we default repeat times to
2761 if (!f_buf || repeat_type == CSA_X_DT_REPEAT_ONETIME)
2764 if (repeat_times == CSA_X_DT_DT_REPEAT_FOREVER) {
2765 strcat(rule_buf, "#0");
2772 if (repeat_times % repeat_nth)
2773 duration = 1 + repeat_times/repeat_nth;
2775 duration = repeat_times/repeat_nth;
2776 sprintf(buf, "#%d", duration);
2778 sprintf(buf, "#%ld", repeat_times);
2780 strcat(rule_buf, buf);
2782 a->recurrence_rule->value->item.string_value = strdup(rule_buf);