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 librararies 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(register char *dest, register char *source) {
173 if ((*dest++ = *source++) == '\n')
178 count_newlines(register char *string) {
184 if (*string++ == '\n')
198 if (type == TTY_Insert)
199 fprintf(stderr, "%s %s",
200 catgets(catd, 1, 1042, "Insert Access Denied: "));
201 else if (type == TTY_Delete)
202 fprintf(stderr, "%s %s",
203 catgets(catd, 1, 1043, "Delete Access Denied: "));
205 fprintf(stderr, "%s %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] == NULL)) {
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 NULL, 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] = NULL;
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] = NULL;
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);
1222 avlist->count = attrs_written;
1223 set_appt_links(avlist);
1232 switch (dow(tick)) {
1234 return (cm_strdup("SU"));
1236 return (cm_strdup("MO"));
1238 return (cm_strdup("TU"));
1240 return (cm_strdup("WE"));
1242 return (cm_strdup("TH"));
1244 return (cm_strdup("FR"));
1247 return (cm_strdup("SA"));
1251 /* this routine is designed to take in an appintment, and generate the
1252 DATAVER4 recurrence rule for that appointment. It is also responsible
1253 for crushing out the old style attributes that may not be inserted
1254 into a newer style data model. */
1257 generate_recurrence_rule(Dtcm_appointment *appt, int version) {
1262 CSA_sint32 repeat_type;
1263 CSA_uint32 repeat_nth;
1264 CSA_uint32 repeat_for;
1268 rule_buf1[0] = '\0';
1269 rule_buf2[0] = '\0';
1271 if (appt->repeat_type && appt->repeat_type->value)
1272 repeat_type = appt->repeat_type->value->item.sint32_value;
1274 repeat_type = CSA_X_DT_REPEAT_ONETIME;
1276 if (appt->repeat_interval && appt->repeat_interval->value)
1277 repeat_nth = appt->repeat_interval->value->item.uint32_value;
1279 if (appt->repeat_times && appt->repeat_times->value)
1280 repeat_for = appt->repeat_times->value->item.uint32_value;
1282 _csa_iso8601_to_tick(appt->time->value->item.date_time_value, &appt_time);
1284 if (!appt->recurrence_rule || !appt->recurrence_rule->value) {
1285 switch (repeat_type) {
1287 case CSA_X_DT_REPEAT_ONETIME :
1288 strcpy(rule_buf1, "D1 ");
1291 case CSA_X_DT_REPEAT_DAILY :
1292 strcpy(rule_buf1, "D1 ");
1295 case CSA_X_DT_REPEAT_WEEKLY :
1296 strcpy(rule_buf1, "W1 ");
1299 case CSA_X_DT_REPEAT_BIWEEKLY :
1300 strcpy(rule_buf1, "W2 ");
1303 case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY :
1305 if (weekofmonth(appt_time, &wk) && wk == 5)
1306 sprintf(rule_buf1, "MP1 1- %s ", dow_str(appt_time));
1308 strcpy(rule_buf1, "MP1 ");
1312 case CSA_X_DT_REPEAT_MONTHLY_BY_DATE :
1313 strcpy(rule_buf1, "MD1 ");
1316 case CSA_X_DT_REPEAT_YEARLY :
1317 strcpy(rule_buf1, "YM1 ");
1320 case CSA_X_DT_REPEAT_MON_TO_FRI :
1321 strcpy(rule_buf1, "W1 MO TU WE TH FR ");
1324 case CSA_X_DT_REPEAT_MONWEDFRI :
1325 strcpy(rule_buf1, "W1 MO WE FR ");
1328 case CSA_X_DT_REPEAT_TUETHUR :
1329 strcpy(rule_buf1, "W1 TU TH ");
1332 case CSA_X_DT_REPEAT_EVERY_NDAY :
1333 sprintf(rule_buf1, "D%ld ", repeat_nth);
1336 case CSA_X_DT_REPEAT_EVERY_NWEEK :
1337 sprintf(rule_buf1, "W%ld ", repeat_nth);
1340 case CSA_X_DT_REPEAT_EVERY_NMONTH :
1341 sprintf(rule_buf1, "MD%ld ", repeat_nth);
1345 if (repeat_for == 0)
1346 strcat(rule_buf2, "#1");
1348 sprintf(rule_buf2, "#%ld", repeat_for);
1350 strcat (rule_buf1, rule_buf2);
1352 appt->attrs = realloc(appt->attrs,
1353 sizeof(CSA_attribute) * (appt->count + 1));
1355 build_new_attrval(&appt->attrs[appt->count],
1356 CSA_ENTRY_ATTR_RECURRENCE_RULE,
1362 set_appt_links(appt);
1365 if (version == DATAVER_ARCHIVE)
1368 /* crush out the old values, if they exist */
1370 if (appt->repeat_type && appt->repeat_type->name) {
1371 free(appt->repeat_type->name);
1372 appt->repeat_type->name = NULL;
1375 if (appt->repeat_times && appt->repeat_times->name) {
1376 free(appt->repeat_times->name);
1377 appt->repeat_times->name = NULL;
1380 if (appt->repeat_interval && appt->repeat_interval->name) {
1381 free(appt->repeat_interval->name);
1382 appt->repeat_interval->name = NULL;
1385 if (appt->repeat_week_num && appt->repeat_week_num->name) {
1386 free(appt->repeat_week_num->name);
1387 appt->repeat_week_num->name = NULL;
1393 * Given a file name, we're going to parse the file and create an appointment
1394 * for the calling routine.
1396 * This function will scan for X number of appointments in the file and add
1397 * each to the linked list unless the validation routine returns an error.
1398 * If this happens, the invalid appointment is still added to the list so the
1399 * calling routine can do further processing if neccessary, but the remaining
1400 * appointments in the file (if any) are not read.
1403 parse_appt_from_file(nl_catd catd, char *file, CmDataList *list, Props *p,
1404 boolean_t(*query)(), void *key_data, int version) {
1406 char line[MAXNAMELEN], *tmp, *key_str, *val_str,
1407 s_buf[MAXNAMELEN], e_buf[MAXNAMELEN],
1408 d_buf[MAXNAMELEN], r_buf[MAXNAMELEN],
1409 f_buf[MAXNAMELEN], *w_buf = NULL;
1411 boolean_t processing_appt = B_FALSE,
1412 processing_what = B_FALSE;
1414 Validate_op valid_op = VALID_APPT;
1415 Parse_key_op pko = NOT_A_KEY;
1416 Dtcm_appointment *appt;
1417 boolean_t found_appt = B_FALSE;
1419 if (!file || *file == '\0' || (fp = fopen(file, "r")) == NULL)
1420 return COULD_NOT_OPEN_FILE;
1422 appt = allocate_appt_struct(appt_write, version, NULL);
1423 load_appt_defaults(appt, p);
1424 while (valid_op == VALID_APPT && fgets(line, MAXNAMELEN - 1, fp)) {
1426 * Point key_str at the first non-space character
1430 if ((*line != ' ' && *line != '\t') || blank_buf(line)) {
1431 processing_what = B_FALSE;
1436 * The check for '*' handles the case when the appt header
1437 * ``** Calendar Appointment **'' has no white space to its
1440 if (*key_str == '\0' ||
1441 (!isspace((u_char)*key_str) && (u_char)*key_str != '*'))
1444 while (*key_str != '\0' && isspace((u_char)*key_str))
1447 pko = identify_parse_key(key_str);
1450 * Remove any ending white space
1452 tmp = strrchr(line, '\0'); --tmp;
1453 while(isspace((u_char)*tmp)) {
1459 * If we're not currently processing the what string and we
1460 * haven't identified a key, continue the loop. Otherwise,
1461 * we've identified a key, so we can't be in the middle of
1462 * processing the what string -- set the flag accordingly.
1465 if (pko == NOT_A_KEY) {
1466 if (!processing_what || what_lines >= 5)
1471 processing_what = B_FALSE;
1476 * Point val_str at the next non-space character after the key
1479 while(*val_str != '\0' && !isspace((u_char)*val_str))
1481 while(*val_str != '\0' && isspace((u_char)*val_str))
1485 * Now, based on the keyword, set the necessary stuff in the
1486 * new appointment structure.
1489 case APPOINTMENT_START:
1490 if (processing_appt) {
1492 * We've reached another appointment. Add the
1493 * current to the linked list, reset flags, and
1494 * check it's validity ...
1496 CmDataListAdd(list, (void *)appt, 0);
1498 valid_op = validate_appt(catd, appt, s_buf,
1499 e_buf, d_buf, dur, w_buf, r_buf, f_buf,
1500 query, key_data, version);
1501 if (valid_op == VALID_APPT) {
1502 appt = allocate_appt_struct(appt_write, version, NULL);
1503 load_appt_defaults(appt, p);
1506 processing_appt = B_FALSE;
1508 processing_appt = B_TRUE;
1510 found_appt = B_TRUE;
1513 memset(s_buf, '\0', MAXNAMELEN);
1514 memset(e_buf, '\0', MAXNAMELEN);
1515 memset(d_buf, '\0', MAXNAMELEN);
1516 memset(r_buf, '\0', MAXNAMELEN);
1517 memset(f_buf, '\0', MAXNAMELEN);
1523 cm_strcpy(d_buf, val_str);
1526 cm_strcpy(s_buf, val_str);
1527 len = cm_strlen(s_buf) - 1;
1528 if (s_buf[len] == 'a' || s_buf[len] == 'p')
1529 cm_strcat(s_buf, "m");
1532 cm_strcpy(e_buf, val_str);
1533 len = cm_strlen(e_buf) - 1;
1534 if (e_buf[len] == 'a' || e_buf[len] == 'p')
1535 cm_strcat(e_buf, "m");
1538 dur = atoi(val_str);
1541 * Check for a unit specification
1543 while(*val_str != '\0' && !isspace((u_char)*val_str))
1545 while(*val_str != '\0' && isspace((u_char)*val_str))
1569 * If we're not currently processing a what string
1570 * and we've got a what value, there were more than one
1571 * what strings in the file - delete the first one and
1574 * Otherwise, we in process, so alloc more space and
1575 * concatinate the new string to the end of the last.
1577 if (!processing_what) {
1580 w_buf = cm_strdup(val_str);
1584 w_buf = (char *)ckalloc(cm_strlen(tmp)
1585 + cm_strlen(key_str) + 2);
1586 cm_strcpy(w_buf, tmp);
1587 cm_strcat(w_buf, "\n");
1588 cm_strcat(w_buf, key_str);
1593 processing_what = B_TRUE;
1596 cm_strcpy(r_buf, val_str);
1599 cm_strcpy(f_buf, val_str);
1602 if ((version >= DATAVER4) ||
1603 (version == DATAVER_ARCHIVE)) {
1604 read_new_appt(fp, &appt, p, version);
1605 generate_recurrence_rule(appt, version);
1606 CmDataListAdd(list, (void *)appt, 0);
1607 processing_appt = B_FALSE;
1608 found_appt = B_TRUE;
1618 * Because the appointment read is only saved when we start reading the
1619 * next appointment, the last appointment (which doesn't have a next)
1620 * will always be left off ... so, providing we're processing an
1621 * appointment -- meaning we've found at least one header -- save the
1622 * appointment in process.
1625 if (found_appt == B_FALSE) {
1627 return(INVALID_DATE);
1630 if (processing_appt) {
1631 CmDataListAdd(list, (void *)appt, 0);
1632 valid_op = validate_appt(catd, appt, s_buf, e_buf, d_buf, dur,
1633 w_buf, r_buf, f_buf, query, key_data,
1639 scrub_attr_list(appt);
1646 growcat(char **source, char *new)
1648 *source = (char *) realloc(*source, strlen(*source) + strlen(new) + 2);
1649 strcat(*source, new);
1653 cat_indented_string(char **catbuf, char *string)
1655 register char *p_str = string;
1658 register char *b_ptr;
1660 nl_count = count_newlines(string);
1662 b_ptr = buf = malloc(strlen(string) + nl_count + 1);
1672 growcat(catbuf, buf);
1678 attrs_to_string(CSA_attribute * attrs, int num_attrs)
1681 CSA_access_list a_ptr;
1682 char *buffer = malloc(1);
1683 char tmp_buf[MAXNAMELEN];
1687 for (i = 0; i < num_attrs; i++) {
1688 if (!attrs[i].name || (attrs[i].value == NULL))
1692 sprintf(tmp_buf, "%s:", attrs[i].name);
1693 switch (attrs[i].value->type) {
1694 case CSA_VALUE_SINT32:
1695 growcat(&buffer, tmp_buf);
1696 sprintf(tmp_buf, "sinteger:%ld\n",
1697 attrs[i].value->item.sint32_value);
1698 growcat(&buffer, tmp_buf);
1701 case CSA_VALUE_UINT32:
1702 growcat(&buffer, tmp_buf);
1703 sprintf(tmp_buf, "uinteger:%ld\n",
1704 attrs[i].value->item.uint32_value);
1705 growcat(&buffer, tmp_buf);
1708 case CSA_VALUE_DATE_TIME:
1709 if (attrs[i].value->item.string_value == NULL)
1712 growcat(&buffer, tmp_buf);
1713 growcat(&buffer, "datetime:");
1714 if (attrs[i].value->item.date_time_value)
1715 cat_indented_string(&buffer,
1716 attrs[i].value->item.string_value);
1717 growcat(&buffer, "\n");
1720 case CSA_VALUE_STRING:
1721 if (attrs[i].value->item.string_value == NULL)
1724 growcat(&buffer, tmp_buf);
1725 growcat(&buffer, "string:");
1726 if (attrs[i].value->item.string_value)
1727 cat_indented_string(&buffer,
1728 attrs[i].value->item.string_value);
1729 growcat(&buffer, "\n");
1731 case CSA_VALUE_CALENDAR_USER:
1732 if (attrs[i].value->item.calendar_user_value == NULL)
1735 growcat(&buffer, tmp_buf);
1736 sprintf(tmp_buf, "caluser:%ld:", attrs[i].value->item.calendar_user_value->user_type);
1737 growcat(&buffer, tmp_buf);
1738 if (attrs[i].value->item.calendar_user_value->user_name)
1739 cat_indented_string(&buffer,
1740 attrs[i].value->item.calendar_user_value->user_name);
1742 growcat(&buffer, "\n");
1745 case CSA_VALUE_REMINDER:
1746 if (attrs[i].value->item.reminder_value->lead_time == NULL)
1749 growcat(&buffer, tmp_buf);
1750 _csa_iso8601_to_duration(attrs[i].value->item.reminder_value->lead_time, &advance_time);
1751 sprintf(tmp_buf, "reminder:%d:", advance_time);
1752 growcat(&buffer, tmp_buf);
1753 if (attrs[i].value->item.reminder_value->reminder_data.data)
1754 cat_indented_string(&buffer,
1755 (char *) attrs[i].value->item.reminder_value->reminder_data.data);
1756 growcat(&buffer, "\n");
1759 case CSA_VALUE_ACCESS_LIST:
1760 if (attrs[i].value->item.access_list_value == NULL)
1763 growcat(&buffer, tmp_buf);
1764 growcat(&buffer, "accesslist:\n");
1765 a_ptr = attrs[i].value->item.access_list_value;
1767 sprintf(tmp_buf, "\t%d:%s\n",
1768 a_ptr->rights, a_ptr->user->user_name);
1769 growcat(&buffer, tmp_buf);
1770 a_ptr = a_ptr->next;
1780 entry_to_attrval_string(CSA_session_handle target, CSA_entry_handle entry)
1784 CSA_attribute_reference *names;
1785 CSA_attribute *attrs_ret;
1786 CSA_uint32 num_attrs, num_attrs_ret;
1788 csa_list_entry_attributes(target, entry, &num_attrs, &names, NULL);
1789 csa_read_entry_attributes(target, entry, num_attrs, names, &num_attrs_ret,
1792 ptr = attrs_to_string(attrs_ret, num_attrs_ret);
1795 ** Need to check that library's memory management will
1796 ** properly free this. Old scheme had a special fn to
1797 ** to it, which took a pointer and a count. - dac
1799 ** [old version: DtCmFreeAttributeValues(attrs, num_attrs); ]
1801 csa_free(attrs_ret);
1808 calendar_to_attrval_string(CSA_session_handle cal)
1811 CSA_uint32 num_attrs, num_attrs_ret;
1812 CSA_attribute_reference *names;
1814 CSA_attribute *attrs_ret;
1816 csa_list_calendar_attributes(cal, &num_attrs, &names, NULL);
1817 csa_read_calendar_attributes(cal, num_attrs, names, &num_attrs_ret,
1820 ptr = attrs_to_string(attrs_ret, num_attrs_ret);
1822 csa_free(attrs_ret);
1829 * Given an appointment structure, return a character string that can be used
1830 * for DnD or written to a file.
1833 parse_appt_to_string(CSA_session_handle target, CSA_entry_handle entry, Props *p, int version) {
1834 char *ret_val, *attr_string;
1835 Dtcm_appointment *appt;
1836 CSA_uint32 na_ret; /* num of attributes actually read */
1837 CSA_attribute a_ret; /* list of attrs actually read */
1839 attr_string = entry_to_attrval_string(target, entry);
1842 * Extract the info we need from the back-end entry
1844 appt = allocate_appt_struct(appt_read,
1848 query_appt_struct(target, entry, appt);
1849 ret_val = parse_attrs_to_string(appt, p, attr_string);
1850 free_appt_struct(&appt);
1857 parse_attrs_to_string(Dtcm_appointment *appt, Props *p, char *attr_string) {
1858 int nlcount, duration, repeat_nth, repeat_wk, wk;
1859 char *whatstr, d_buf[MAXNAMELEN],
1860 s_buf[MAXNAMELEN], e_buf[MAXNAMELEN], w_buf[MAXNAMELEN],
1861 r_buf[MAXNAMELEN], f_buf[MAXNAMELEN], *appt_what,
1863 time_t tick, end_tick = 0;
1864 CSA_sint32 repeat_type;
1865 CSA_uint32 repeat_times;
1866 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";
1874 _csa_iso8601_to_tick(appt->time->value->item.date_time_value, &tick);
1876 if (appt->end_time->value != NULL)
1877 _csa_iso8601_to_tick(appt->end_time->value->item.\
1878 date_time_value, &end_tick);
1880 appt_what = appt->what->value->item.string_value;
1882 if (appt->repeat_type && appt->repeat_type->value)
1883 repeat_type = appt->repeat_type->value->item.sint32_value;
1885 if (appt->repeat_times && appt->repeat_times->value)
1886 repeat_times = appt->repeat_times->value->item.uint32_value;
1890 if (appt->repeat_interval && appt->repeat_interval->value)
1891 repeat_nth = appt->repeat_interval->value->item.uint32_value;
1895 if (appt->repeat_week_num && appt->repeat_week_num->value)
1896 repeat_wk = appt->repeat_week_num->value->item.sint32_value;
1901 * Format the date and start/stop strings
1903 format_tick(tick, ORDER_MDY, SEPARATOR_SLASH, d_buf);
1904 format_time(tick, get_int_prop(p, CP_DEFAULTDISP), s_buf);
1905 format_time(end_tick, get_int_prop(p, CP_DEFAULTDISP), e_buf);
1908 * Handle the what string
1910 whatstr = appt_what;
1911 if ((nlcount = count_newlines(appt_what)) > 0) {
1912 whatstr = (char *)ckalloc(cm_strlen(appt_what) + nlcount + 1);
1913 copy_and_pad_newlines(whatstr, appt_what);
1915 if (whatstr && !blank_buf(whatstr)) {
1916 cm_strcat(w_buf, whatstr);
1917 cm_strcat(w_buf, "\n\t");
1921 * Repeat and For stuff
1923 cm_strcpy(r_buf, periodstr_from_period(repeat_type, repeat_nth));
1924 if (repeat_type == CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY) {
1925 if (weekofmonth(tick, &wk) && wk == 4) {
1926 if (repeat_wk == -1)
1927 strcat(r_buf, ", last");
1928 else if (repeat_wk == 4)
1929 strcat(r_buf, ", 4th");
1933 sprintf(f_buf, "%ld", repeat_times);
1936 * Put it all together
1939 b_ptr = malloc(cm_strlen(d_buf) +
1940 (2 * cm_strlen(CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER)) +
1946 cm_strlen(format_string) +
1947 cm_strlen(attr_string) +
1950 sprintf(b_ptr, format_string,
1951 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
1953 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
1954 d_buf, s_buf, e_buf, r_buf, f_buf, w_buf);
1961 /* This routine takes a list of buffers that represent a number
1962 of message attachments, and glues them into a mime format
1963 message for eventual submission to the dtmail mailer. These
1964 objects do nor *need* to be cm appointment objects. */
1967 create_rfc_message(char *address_list,
1969 char **appointment_objects,
1974 /* do *not* put these header strings in a message catalog.
1975 These are invariants specified by MIME */
1977 char *address_header = "To: ";
1978 char *subject_header = "Subject: ";
1979 char *x_content_name = "X-Content-Name: CalendarAppointment\n";
1980 char *content_label = "Mime-Version: 1.0\nContent-Type: multipart/mixed;boundary=";
1981 char divider_string[32];
1985 boolean_t done = B_FALSE;
1986 char *return_buffer;
1988 /* A MIME mesage is a rather specialized beast. It consists of
1989 a series of headers describing the mail message, followed by
1990 the message, and then followed by a set of attachments.
1991 Each attachments is separated by a magic unique string
1992 flagging the boundaries between the attached objects. The
1993 first header is the address list. The second header is the
1994 subject line for the message. The third header describes the
1995 MIME version of the message. The forth describes the type of
1996 information within the message and the magic string used for
1999 Each division is two dashes, the unique string, and a newline
2000 The last dividing string is trailed by two more dashes. */
2003 /* we need to generate a unique dividing string that will
2004 not be found in any of the parts of the MIME message.
2005 The following printf will be tried until it generates
2006 something that isn't in any of the body parts. */
2008 while (done != B_TRUE) {
2009 sprintf(divider_string, "%x_%x-%x_%x-%x_%x", rand(), rand(),
2010 rand(), rand(), rand(), rand());
2014 for (i = 0; i < num_objects; i++) {
2015 if (strstr(appointment_objects[i], divider_string) != NULL)
2020 buffer_size = strlen(address_header) +
2021 strlen(address_list) +
2023 strlen(subject_header) +
2026 strlen(content_label) + 2 +
2027 (num_objects + 2) * strlen(divider_string) +
2028 /* one definition copy,
2029 one terminating copy,
2033 5 + strlen(divider_string) + /* empty body part */
2035 num_objects * strlen(x_content_name) +
2036 /* one X-Content-Name
2040 (2 * num_objects) + 4 + /* bracketing on unique
2042 (num_objects * 3) + 2; /* newlines...3 per
2045 terminating boundary */
2047 for (i = 0; i < num_objects; i++)
2048 buffer_size += strlen(appointment_objects[i]);
2050 /* extra byte is added for null char */
2051 return_buffer = (char *)calloc(buffer_size + 1, 1);
2053 sprintf(return_buffer, "%s%s\n%s%s\n%s%s\n\n",
2062 * Add an empty body part. This is a hack to get dtmail to
2063 * display the object(s) as an attachment.
2065 strcat(return_buffer, "\n--");
2066 strcat(return_buffer, divider_string);
2067 strcat(return_buffer, "\n\n");
2069 for (i = 0; i <num_objects; i++) {
2070 strcat(return_buffer, "\n--");
2071 strcat(return_buffer, divider_string);
2072 strcat(return_buffer, "\n");
2073 strcat(return_buffer, x_content_name);
2074 strcat(return_buffer, "\n");
2075 strcat(return_buffer, appointment_objects[i]);
2077 strcat(return_buffer, "\n--");
2078 strcat(return_buffer, divider_string);
2079 strcat(return_buffer, "--");
2080 strcat(return_buffer, "\n");
2082 return(return_buffer);
2086 appointments_to_file(CSA_session_handle target, CSA_entry_handle *appointment_list,
2093 FILE *f_ptr = fopen(file_name, "w");
2101 fprintf(f_ptr, "DTCM Archive 1.0\n");
2102 for (i = 0; i < num_appts; i++) {
2103 entry_string = entry_to_attrval_string(target, appointment_list[i]);
2105 fprintf(f_ptr, "\n-%s:string:begin\n%s%s:string:end\n\n",
2106 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
2108 CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER);
2119 * NOTE!! These are strings used in versions 1-4 - the repeat strings read
2120 * from a file or passed to the cm_tty_insert routine are checked against
2121 * these strings as well as the V5 API strings.
2123 static char *periodstrings[] = {
2130 "Monthly By Weekday",
2135 "Monday thru Friday",
2137 "Tuesday, Thursday",
2143 str_to_period(char *ps, CSA_sint32 *repeat_type, int *repeat_nth) {
2144 boolean_t compute_times = B_FALSE;
2145 char *ps2, *ptr, *ptr2, *unit;
2147 *repeat_type = NULL;
2152 if (strcasecmp(ps, periodstrings[0]) == 0)
2153 *repeat_type = CSA_X_DT_REPEAT_ONETIME;
2154 else if (strcasecmp(ps, periodstrings[1]) == 0)
2155 *repeat_type = CSA_X_DT_REPEAT_DAILY;
2156 else if (strcasecmp(ps, periodstrings[2]) == 0)
2157 *repeat_type = CSA_X_DT_REPEAT_WEEKLY;
2158 else if (strcasecmp(ps, periodstrings[3]) == 0)
2159 *repeat_type = CSA_X_DT_REPEAT_BIWEEKLY;
2160 else if (strcasecmp(ps, periodstrings[4]) == 0)
2161 *repeat_type = CSA_X_DT_REPEAT_MONTHLY_BY_DATE;
2162 else if (strcasecmp(ps, periodstrings[5]) == 0)
2163 *repeat_type = CSA_X_DT_REPEAT_YEARLY;
2164 else if (strncasecmp(ps, periodstrings[6],
2165 strlen(periodstrings[6])) == 0)
2166 *repeat_type = CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY;
2167 else if (strcasecmp(ps, periodstrings[10]) == 0)
2168 *repeat_type = CSA_X_DT_REPEAT_OTHER;
2169 else if (strcasecmp(ps, periodstrings[11]) == 0)
2170 *repeat_type = CSA_X_DT_REPEAT_MON_TO_FRI;
2171 else if (strcasecmp(ps, periodstrings[12]) == 0)
2172 *repeat_type = CSA_X_DT_REPEAT_MONWEDFRI;
2173 else if (strcasecmp(ps, periodstrings[13]) == 0)
2174 *repeat_type = CSA_X_DT_REPEAT_TUETHUR;
2175 else if (strcasecmp(ps, periodstrings[14]) == 0)
2176 *repeat_type = CSA_X_DT_REPEAT_WEEKDAYCOMBO;
2177 else if (strncasecmp(ps, periodstrings[15], strlen(periodstrings[15])) == 0) {
2178 compute_times = B_TRUE;
2181 *repeat_type = CSA_X_DT_REPEAT_ONETIME;
2183 if ((compute_times) && (unit = strchr(ps, ' '))) {
2184 while (*unit == ' ')
2186 ps2 = cm_strdup(unit);
2187 ptr = strchr(ps2, ' ');
2197 *repeat_nth = atoi(ps2);
2198 if (strcasecmp(ptr, periodstrings[7]) == 0) {
2199 *repeat_type = CSA_X_DT_REPEAT_EVERY_NDAY;
2201 else if (strcasecmp(ptr, periodstrings[8]) == 0) {
2202 *repeat_type = CSA_X_DT_REPEAT_EVERY_NWEEK;
2204 else if (strcasecmp(ptr, periodstrings[9]) == 0) {
2205 *repeat_type = CSA_X_DT_REPEAT_EVERY_NMONTH;
2212 periodstr_from_period(CSA_sint32 repeat_type, int repeat_nth) {
2213 static char pstr[80];
2215 switch (repeat_type) {
2216 case CSA_X_DT_REPEAT_ONETIME:
2217 sprintf(pstr, "%s", periodstrings[0]);
2219 case CSA_X_DT_REPEAT_DAILY:
2220 sprintf(pstr, "%s", periodstrings[1]);
2222 case CSA_X_DT_REPEAT_WEEKLY:
2223 sprintf(pstr, "%s", periodstrings[2]);
2225 case CSA_X_DT_REPEAT_BIWEEKLY:
2226 sprintf(pstr, "%s", periodstrings[3]);
2228 case CSA_X_DT_REPEAT_MONTHLY_BY_DATE:
2229 sprintf(pstr, "%s", periodstrings[4]);
2231 case CSA_X_DT_REPEAT_YEARLY:
2232 sprintf(pstr, "%s", periodstrings[5]);
2234 case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY:
2235 sprintf(pstr, "%s", periodstrings[6]);
2237 case CSA_X_DT_REPEAT_EVERY_NDAY:
2238 sprintf(pstr, "Every %ld %s", repeat_nth, periodstrings[7]);
2240 case CSA_X_DT_REPEAT_EVERY_NWEEK:
2241 sprintf(pstr, "Every %ld %s", repeat_nth, periodstrings[8]);
2243 case CSA_X_DT_REPEAT_EVERY_NMONTH:
2244 sprintf(pstr, "Every %ld %s", repeat_nth, periodstrings[9]);
2246 case CSA_X_DT_REPEAT_OTHER:
2247 sprintf(pstr, "%s", periodstrings[10]);
2249 case CSA_X_DT_REPEAT_MON_TO_FRI:
2250 sprintf(pstr, "%s", periodstrings[11]);
2252 case CSA_X_DT_REPEAT_MONWEDFRI:
2253 sprintf(pstr, "%s", periodstrings[12]);
2255 case CSA_X_DT_REPEAT_TUETHUR:
2256 sprintf(pstr, "%s", periodstrings[13]);
2258 case CSA_X_DT_REPEAT_WEEKDAYCOMBO:
2259 sprintf(pstr, "%s", periodstrings[14]);
2262 sprintf(pstr, "Unknown repeat type");
2270 * NOTE!! These first set of these strings are used in versions 1-4 - the
2271 * privacy strings read from a file or passed to the cm_tty_insert routine are
2272 * checked against these strings as well as the V5 API strings and the new
2275 static char *privacy_strs_old[] = {
2276 "Show Time And Text",
2281 static char *privacy_strs[] = {
2282 "Others See Time And Text",
2283 "Others See Time Only",
2284 "Others See Nothing"
2287 static char *privacy_strs_411[] = {
2294 privacy_str_old(int op) {
2295 if (op >= 0 && op <= 2)
2296 return privacy_strs_old[op];
2301 privacy_str(int op) {
2302 if (op >= 0 && op <= 2)
2303 return privacy_strs[op];
2312 repeat_strs[ONE_TIME] = strdup(catgets(catd, 1, 852, "One Time"));
2313 repeat_strs[DAILY] = strdup(catgets(catd, 1, 853, "Daily"));
2314 repeat_strs[WEEKLY] = strdup(catgets(catd, 1, 854, "Weekly"));
2315 repeat_strs[EVERY_TWO_WEEKS] =
2316 strdup(catgets(catd, 1, 855, "Every Two Weeks"));
2317 repeat_strs[MONTHLY_BY_DATE] =
2318 strdup(catgets(catd, 1, 856, "Monthly By Date"));
2319 repeat_strs[MONTHLY_BY_WEEKDAY] =
2320 strdup(catgets(catd, 1, 857, "Monthly By Weekday"));
2321 repeat_strs[YEARLY] =
2322 strdup(catgets(catd, 1, 858, "Yearly"));
2323 repeat_strs[MONDAY_THRU_FRIDAY] =
2324 strdup(catgets(catd, 1, 859, "Monday Thru Friday"));
2325 repeat_strs[MON_WED_FRI] =
2326 strdup(catgets(catd, 1, 860, "Mon, Wed, Fri"));
2327 repeat_strs[TUESDAY_THURSDAY] =
2328 strdup(catgets(catd, 1, 861, "Tuesday, Thursday"));
2329 repeat_strs[REPEAT_EVERY] =
2330 strdup(catgets(catd, 1, 862, "Repeat Every..."));
2338 if (!repeat_strs[0])
2339 init_repeat_strs(catd, repeat_strs);
2341 if (op >= ONE_TIME && op <= REPEAT_EVERY)
2342 return repeat_strs[op];
2347 privacy_str_411(int op) {
2348 if (op >= 0 && op <= 2)
2349 return privacy_strs_411[op];
2356 Repeat_scope_menu_op op)
2358 if (!repeat_scope_strs[0]) {
2359 repeat_scope_strs[REPEAT_DAYS] =
2360 strdup(catgets(catd, 1, 994, "days"));
2361 repeat_scope_strs[REPEAT_WEEKS] =
2362 strdup(catgets(catd, 1, 995, "weeks"));
2363 repeat_scope_strs[REPEAT_MONTHS] =
2364 strdup(catgets(catd, 1, 997, "months"));
2367 if (op >= REPEAT_DAYS && op <= REPEAT_MONTHS)
2368 return repeat_scope_strs[op];
2373 separator_str(SeparatorType op) {
2374 if (op >= SEPARATOR_BLANK && op <= SEPARATOR_DASH)
2375 return separator_strs[op];
2380 timescopestring_to_tick(char *str) {
2381 if (strcasecmp(time_scope_strs[1], str) == 0)
2383 else if (strcasecmp(time_scope_strs[2], str) == 0)
2389 time_scope_str(Time_scope_menu_op op) {
2390 if (op >= TIME_MINS && op <= TIME_DAYS)
2391 return time_scope_strs[op];
2396 time_scope_str_i18n(
2398 Time_scope_menu_op op)
2400 if (!time_scope_strs_i18n[0]) {
2401 time_scope_strs_i18n[TIME_MINS] =
2402 strdup(catgets(catd, 1, 877, "Mins"));
2403 time_scope_strs_i18n[TIME_HRS] =
2404 strdup(catgets(catd, 1, 878, "Hrs"));
2405 time_scope_strs_i18n[TIME_DAYS] =
2406 strdup(catgets(catd, 1, 879, "Days"));
2409 if (op >= TIME_MINS && op <= TIME_DAYS)
2410 return time_scope_strs_i18n[op];
2415 ** Determine whether or not the time passed is a valid format
2418 valid_time(Props *p, char *time_str) {
2420 register int num_minutes = 0, num_colons = 0;
2421 boolean_t after_colon = B_FALSE;
2422 DisplayType dt = get_int_prop(p, CP_DEFAULTDISP);
2424 for (ptr = time_str; ptr != NULL && *ptr != '\0'; ptr++) {
2427 after_colon = B_TRUE;
2428 if ((++num_colons) > 1)
2430 if (*(ptr+1) == '\0')
2433 else if (*ptr != ' ' && (*ptr < '0' || *ptr > '9') )
2435 if ((after_colon) && (*ptr != ':'))
2437 if (num_minutes > 2)
2439 else if (num_minutes == 2) {
2441 if (strncasecmp(ptr, "am", 2) == 0 ||
2442 strncasecmp(ptr, "pm", 2) == 0)
2447 else if (dt == HOUR24) {
2448 if (*ptr != ' ' && (*ptr < '0' || *ptr > '9') )
2450 if (++num_minutes > 4)
2454 if (dt == HOUR12 && ((int)atoi(time_str) > 12))
2456 else if ((dt == HOUR24)
2457 && ((int)atoi(time_str) > 2359))
2464 * This method will validate the passed appointment data.
2467 * Date string incorrect INVALID_DATE
2468 * Tick for start is < 0 INVALID_START
2469 * Tick for stop is < 0 INVALID_STOP
2470 * Blank date MISSING_DATE
2471 * End time but no start time MISSING_START
2472 * Blank what with no times MISSING_WHAT
2473 * Period = single & for > 0 REPEAT_FOR_MISMATCH
2474 * Period != single & for = 0 REPEAT_FOR_MISMATCH
2476 * Note the function pointer passed to this function - if the end time is
2477 * before the start time, this function will be executed and should return
2478 * B_TRUE if the appointment should be scheduled to the next day, B_FALSE if
2479 * it should be canceled.
2482 validate_appt(nl_catd catd, Dtcm_appointment *a, char *s_buf, char *e_buf,
2483 char *d_buf, int dur, char *w_buf, char *r_buf, char *f_buf,
2484 boolean_t(*query)(void*), void *key_data, int version) {
2487 if ((op = validate_dssw(a, s_buf, e_buf, d_buf, dur, w_buf, query,
2488 key_data)) != VALID_APPT)
2490 if ((op = validate_rfp(catd, a, r_buf, f_buf, version)) != VALID_APPT)
2492 if ((op = validate_reminders(a)) != VALID_APPT)
2499 validate_dssw(Dtcm_appointment *a, char *s_buf, char *e_buf, char *d_buf,
2500 int dur, char *w_buf, boolean_t(*query)(), void *key_data) {
2502 char buf[MAXNAMELEN];
2505 if (blank_buf(d_buf))
2506 return MISSING_DATE;
2508 a->time->value->item.date_time_value = malloc(BUFSIZ);
2509 _csa_tick_to_iso8601(0, a->time->value->item.date_time_value);
2511 a->show_time->value->item.sint32_value = B_TRUE;
2513 if (w_buf && w_buf[0] != '\0') {
2514 a->what->value->item.string_value = cm_strdup(w_buf);
2515 expand_esc_chars(a->what->value->item.string_value);
2517 a->what->value->item.string_value = NULL;
2519 if (!blank_buf(s_buf)) {
2521 * We have something in the start buffer, is it valid?
2523 sprintf(buf, "%s %s", d_buf, s_buf);
2524 if ((appt_time = cm_getdate(buf, NULL)) < 0)
2525 return INVALID_START;
2527 _csa_tick_to_iso8601(appt_time, a->time->value->item.date_time_value);
2529 * Okay, we have a valid start time - do we have a duration
2534 * Duration is specified - add duration to start time
2536 end_tick = appt_time + dur;
2537 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2538 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2539 } else if (!blank_buf(e_buf)) {
2541 * No duration, but something in the end buffer. If
2542 * it's valid set the end tick to it's value.
2544 sprintf(buf, "%s %s", d_buf, e_buf);
2545 if ((end_tick = cm_getdate(buf, NULL)) < 0)
2546 return INVALID_STOP;
2547 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2548 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2551 * No duration or end buffer - set end_tick to starting
2552 * tick and duration to 0
2554 a->end_time->value->item.date_time_value =
2555 strdup(a->time->value->item.date_time_value);
2557 } else if (dur > 0 || !blank_buf(e_buf))
2558 return MISSING_START;
2560 if (blank_buf(a->what->value->item.string_value))
2561 return MISSING_WHAT;
2564 * If we're here, there was a date with no start or stop time,
2565 * so set time to magic time (3:41 am - don't ask where that
2566 * came from, 'cause I certainly don't know) and make sure the
2567 * date was correct. If so, set duration to 1 minute and
2568 * showtime to false.
2570 sprintf(buf, "%s 3:41am", d_buf);
2572 if ((appt_time = cm_getdate(buf, NULL)) < 0)
2573 return INVALID_DATE;
2575 a->time->value->item.date_time_value = malloc(BUFSIZ);
2576 _csa_tick_to_iso8601(appt_time, a->time->value->item.date_time_value);
2578 end_tick = appt_time + minsec;
2580 a->end_time->value->item.date_time_value = malloc(BUFSIZ);
2581 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2583 a->show_time->value->item.sint32_value = B_FALSE;
2587 * Finally, if the ending tick is before the starting tick, execute the
2588 * passed function which should return B_TRUE if we should schedule
2589 * this into the next day and B_FALSE if not.
2591 * This allows for methods calling this function to be UI oriented or
2592 * command line oriented as they can "query" the user appropriately.
2594 if (end_tick < appt_time) {
2595 if ((*query)(key_data) == B_TRUE) {
2596 while (end_tick < appt_time)
2599 _csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
2610 validate_reminders(Dtcm_appointment *a) {
2617 Dtcm_appointment *a,
2625 repeat_forever = False;
2626 CSA_sint32 repeat_type;
2627 CSA_uint32 repeat_times = 0;
2631 str_to_period(r_buf, &repeat_type, &repeat_nth);
2632 if (repeat_type == CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY) {
2633 r_buf += strlen(periodstrings[6]);
2634 while(*r_buf != '\0' && !isspace((u_char)*r_buf))
2636 while(*r_buf != '\0' && isspace((u_char)*r_buf))
2639 if (strncasecmp(r_buf, "last", 4) != 0)
2640 repeat_wk = atoi(r_buf);
2645 /* Repeat forever is represented by either:
2646 f_buf == ``forever'' or
2648 If it is a CSA_X_DT_REPEAT_ONETIME then
2651 if (strcasecmp(f_buf, catgets(catd, 1, 876, "forever")) == 0) {
2652 repeat_times = CSA_X_DT_DT_REPEAT_FOREVER;
2653 repeat_forever = True;
2655 repeat_times = atoi(f_buf);
2656 if (repeat_times == CSA_X_DT_DT_REPEAT_FOREVER &&
2657 repeat_type != CSA_X_DT_REPEAT_ONETIME)
2658 repeat_forever = True;
2662 /* If it is a onetime event then it cannot repeat.
2663 * If it is a repeating event then repeat_times must be greater
2664 * than 0 unless it is supposed to repeat forever.
2666 if (((repeat_type != CSA_X_DT_REPEAT_ONETIME) &&
2667 (repeat_times == 0) && (repeat_forever != True)) ||
2668 ((repeat_type == CSA_X_DT_REPEAT_ONETIME) &&
2669 ((repeat_times != 0) || (repeat_forever == True))))
2670 return REPEAT_FOR_MISMATCH;
2672 if (a->repeat_type && a->repeat_type->value)
2673 a->repeat_type->value->item.sint32_value = repeat_type;
2674 if (a->repeat_times && a->repeat_times->value)
2675 a->repeat_times->value->item.uint32_value = repeat_times;
2676 if (a->repeat_interval && a->repeat_interval->value)
2677 a->repeat_interval->value->item.uint32_value = repeat_nth;
2678 if (a->repeat_week_num && a->repeat_week_num->value)
2679 a->repeat_week_num->value->item.sint32_value = repeat_wk;
2681 /* If we are less than data version 4, we're done. */
2682 if (version < DATAVER4)
2685 /* Data version 4 appts use a recurrence rule. */
2686 memset (rule_buf, 0, 32);
2688 switch(repeat_type) {
2689 case CSA_X_DT_REPEAT_ONETIME:
2690 case CSA_X_DT_REPEAT_DAILY:
2691 strcpy(rule_buf, "D1 ");
2693 case CSA_X_DT_REPEAT_WEEKLY:
2694 strcpy(rule_buf, "W1 ");
2696 case CSA_X_DT_REPEAT_BIWEEKLY:
2697 strcpy(rule_buf, "W2 ");
2699 case CSA_X_DT_REPEAT_MONTHLY_BY_DATE:
2700 strcpy(rule_buf, "MD1 ");
2702 case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY: {
2706 if (a && a->time && a->time->value) {
2707 _csa_iso8601_to_tick(
2708 a->time->value->item.date_time_value,
2713 * The current behavior of cm/dtcm is that if an appt is
2714 * scheduled for the 5 wk of the month, it repeats on the
2715 * last week of the month.
2717 if (tick && weekofmonth(tick, &wk) && wk == 5)
2718 sprintf(rule_buf, "MP1 1- %s ", dow_str(tick));
2720 strcpy(rule_buf, "MP1 ");
2723 case CSA_X_DT_REPEAT_YEARLY:
2724 strcpy(rule_buf, "YM1 ");
2726 case CSA_X_DT_REPEAT_MON_TO_FRI:
2727 strcpy(rule_buf, "W1 MO TU WE TH FR ");
2729 case CSA_X_DT_REPEAT_MONWEDFRI:
2730 strcpy(rule_buf, "W1 MO WE FR ");
2732 case CSA_X_DT_REPEAT_TUETHUR:
2733 strcpy(rule_buf, "W1 TU TH ");
2735 case CSA_X_DT_REPEAT_EVERY_NDAY:
2736 sprintf(rule_buf, "D%d ", repeat_nth);
2737 repeat_every = True;
2739 case CSA_X_DT_REPEAT_EVERY_NWEEK:
2740 sprintf(rule_buf, "W%d ", repeat_nth);
2741 repeat_every = True;
2743 case CSA_X_DT_REPEAT_EVERY_NMONTH:
2744 sprintf(rule_buf, "MD%d ", repeat_nth);
2745 repeat_every = True;
2751 /* If the for buffer is NULL then we default to repeating one time.
2752 * If the repeat_type is onetime then we default repeat times to
2755 if (!f_buf || repeat_type == CSA_X_DT_REPEAT_ONETIME)
2758 if (repeat_times == CSA_X_DT_DT_REPEAT_FOREVER) {
2759 strcat(rule_buf, "#0");
2766 if (repeat_times % repeat_nth)
2767 duration = 1 + repeat_times/repeat_nth;
2769 duration = repeat_times/repeat_nth;
2770 sprintf(buf, "#%d", duration);
2772 sprintf(buf, "#%ld", repeat_times);
2774 strcat(rule_buf, buf);
2776 a->recurrence_rule->value->item.string_value = strdup(rule_buf);