diff options
Diffstat (limited to 'calendar')
-rw-r--r-- | calendar/ChangeLog | 6 | ||||
-rw-r--r-- | calendar/Makefile.am | 2 | ||||
-rw-r--r-- | calendar/alarm.c | 145 | ||||
-rw-r--r-- | calendar/alarm.h | 10 | ||||
-rw-r--r-- | calendar/cal-util/calobj.c | 18 | ||||
-rw-r--r-- | calendar/cal-util/calobj.h | 7 | ||||
-rw-r--r-- | calendar/calendar.c | 69 | ||||
-rw-r--r-- | calendar/calendar.h | 1 | ||||
-rw-r--r-- | calendar/calobj.c | 18 | ||||
-rw-r--r-- | calendar/calobj.h | 7 | ||||
-rw-r--r-- | calendar/eventedit.c | 4 | ||||
-rw-r--r-- | calendar/gnome-cal.c | 157 | ||||
-rw-r--r-- | calendar/gnome-cal.h | 1 | ||||
-rw-r--r-- | calendar/gui/Makefile.am | 2 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/alarm.c | 145 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/alarm.h | 10 | ||||
-rw-r--r-- | calendar/gui/alarm.c | 145 | ||||
-rw-r--r-- | calendar/gui/alarm.h | 10 | ||||
-rw-r--r-- | calendar/gui/calendar.c | 69 | ||||
-rw-r--r-- | calendar/gui/calendar.h | 1 | ||||
-rw-r--r-- | calendar/gui/eventedit.c | 4 | ||||
-rw-r--r-- | calendar/gui/gnome-cal.c | 157 | ||||
-rw-r--r-- | calendar/gui/gnome-cal.h | 1 | ||||
-rw-r--r-- | calendar/gui/main.c | 36 | ||||
-rw-r--r-- | calendar/main.c | 36 | ||||
-rw-r--r-- | calendar/pcs/calobj.c | 18 | ||||
-rw-r--r-- | calendar/pcs/calobj.h | 7 | ||||
-rw-r--r-- | calendar/timeutil.c | 2 |
28 files changed, 1033 insertions, 55 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 1bd14e83a0..a38f7e90d9 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,9 @@ + +1998-04-17 Miguel de Icaza <miguel@nuclecu.unam.mx> + + * alarm.c (alarm_kill, alarm_init, alarm_add): Implement the alarm + management framework. + 1998-04-17 Federico Mena Quintero <federico@nuclecu.unam.mx> * calobj.c (ical_new): Added mandatory status property. diff --git a/calendar/Makefile.am b/calendar/Makefile.am index b762dae475..9728a02d4b 100644 --- a/calendar/Makefile.am +++ b/calendar/Makefile.am @@ -9,6 +9,8 @@ INCLUDES = \ bin_PROGRAMS = gnomecal gnomecal_SOURCES = \ + alarm.c \ + alarm.h \ calendar.c \ calendar.h \ calobj.c \ diff --git a/calendar/alarm.c b/calendar/alarm.c new file mode 100644 index 0000000000..bb5c4c1b8d --- /dev/null +++ b/calendar/alarm.c @@ -0,0 +1,145 @@ +/* + * Alarm handling for the GNOME Calendar. + * + * (C) 1998 the Free Software Foundation + * + * Author: Miguel de Icaza (miguel@kernel.org) + */ +#include <config.h> +#include <time.h> +#include <gnome.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/time.h> +#include "alarm.h" + +/* The pipes used to notify about an alarm */ +int alarm_pipes [2]; + +/* The list of pending alarms */ +static GList *alarms; + +static void *head_alarm; + +typedef struct { + time_t activation_time; + AlarmFunction fn; + void *closure; +} AlarmRecord; + +/* + * SIGALRM handler. Notifies the callback about the alarm + */ +static void +alarm_activate () +{ + char c = 0; + + printf ("ALARMA!\n"); + write (alarm_pipes [1], &c, 1); +} + +static void +alarm_ready (void *closure, int fd, GdkInputCondition cond) +{ + AlarmRecord *ar = head_alarm; + char c; + + if (read (alarm_pipes [0], &c, 1) != 1) + return; + + if (ar == NULL){ + g_warning ("Empty events. This should not happen\n"); + return; + } + (*ar->fn)(ar->activation_time, ar->closure); + alarms = g_list_remove (alarms, head_alarm); + if (alarms) + head_alarm = alarms->data; + else + head_alarm = NULL; + g_free (ar); +} + +static int +alarm_compare_by_time (gpointer a, gpointer b) +{ + AlarmRecord *ara = a; + AlarmRecord *arb = b; + time_t diff; + + diff = ara->activation_time - arb->activation_time; + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; +} + +void +alarm_add (time_t alarm_time, AlarmFunction fn, void *closure) +{ + time_t now = time (NULL); + AlarmRecord *ar; + + /* If it already expired, do not add it */ + if (alarm_time < now) + return; + + ar = g_new0 (AlarmRecord, 1); + ar->activation_time = alarm_time; + ar->fn = fn; + ar->closure = closure; + + alarms = g_list_insert_sorted (alarms, ar, alarm_compare_by_time); + + /* If first alarm is not the previous first alarm, reschedule SIGALRM */ + if (head_alarm != alarms->data){ + struct itimerval itimer; + int v; + + /* Set the timer to disable upon activation */ + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = 0; + itimer.it_value.tv_sec = alarm_time - now; + itimer.it_value.tv_usec = 0; + v = setitimer (ITIMER_REAL, &itimer, NULL); + head_alarm = alarms->data; + } +} + +void +alarm_kill (void *closure_key) +{ + GList *p; + + for (p = alarms; p; p = p->next){ + AlarmRecord *ar = p->data; + + if (ar->closure == closure_key){ + alarms = g_list_remove (alarms, p->data); + if (alarms) + head_alarm = alarms->data; + else + head_alarm = NULL; + return; + } + } +} + +void +alarm_init (void) +{ + struct sigaction sa; + int flags; + + pipe (alarm_pipes); + + /* set non blocking mode */ + fcntl (alarm_pipes [0], F_GETFL, &flags); + fcntl (alarm_pipes [0], F_SETFL, flags | O_NONBLOCK); + gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, 0); + + /* Setup the signal handler */ + sa.sa_handler = alarm_activate; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction (SIGALRM, &sa, NULL); +} + diff --git a/calendar/alarm.h b/calendar/alarm.h new file mode 100644 index 0000000000..39f7281ce7 --- /dev/null +++ b/calendar/alarm.h @@ -0,0 +1,10 @@ +#ifndef ALARM_H +#define ALARM_H + +typedef void (*AlarmFunction)(time_t time, void *closuse); + +void alarm_init (void); +void alarm_add (time_t alarm_time, AlarmFunction fn, void *closure); +void alarm_kill (void *closure); + +#endif diff --git a/calendar/cal-util/calobj.c b/calendar/cal-util/calobj.c index 47d8c8a97e..624a179b05 100644 --- a/calendar/cal-util/calobj.c +++ b/calendar/cal-util/calobj.c @@ -1102,3 +1102,21 @@ ical_object_compute_end (iCalObject *ico) ico->recur->_enddate = 0; ical_object_generate_events (ico, ico->dtstart, 0, duration_callback, &count); } + +int +alarm_compute_offset (CalendarAlarm *a) +{ + if (!a->enabled) + return -1; + switch (a->units){ + case ALARM_MINUTES: + a->offset = a->count * 60; + break; + case ALARM_HOURS: + a->offset = a->count * 3600; + break; + case ALARM_DAYS: + a->offset = a->count * 24 * 3600; + } + return a->offset; +} diff --git a/calendar/cal-util/calobj.h b/calendar/cal-util/calobj.h index 26b0acf35b..60483c68ad 100644 --- a/calendar/cal-util/calobj.h +++ b/calendar/cal-util/calobj.h @@ -32,6 +32,10 @@ typedef struct { int count; enum AlarmUnit units; char *data; + + /* Does not get saved, internally used */ + time_t offset; + time_t trigger; /* Widgets */ void *w_count; /* A GtkEntry */ @@ -180,6 +184,9 @@ void ical_object_generate_events (iCalObject *ico, time_t start, time /* Computes the enddate field of the recurrence based on the duration */ void ical_object_compute_end (iCalObject *ico); +/* Returns the number of seconds configured to trigger the alarm in advance to an event */ +int alarm_compute_offset (CalendarAlarm *a); + END_GNOME_DECLS #endif diff --git a/calendar/calendar.c b/calendar/calendar.c index be45bb04bb..28f37f78eb 100644 --- a/calendar/calendar.c +++ b/calendar/calendar.c @@ -18,6 +18,9 @@ #include "timeutil.h" #include "versit/vcc.h" +/* Our day range */ +time_t calendar_day_begin, calendar_day_end; + Calendar * calendar_new (char *title) { @@ -29,12 +32,71 @@ calendar_new (char *title) return cal; } +static void +try_add (iCalObject *ico, CalendarAlarm *alarm, time_t start, time_t end) +{ + alarm->trigger = start-alarm->offset; + + if (alarm->trigger < calendar_day_begin) + return; + if (alarm->trigger > calendar_day_end) + return; + alarm_add (alarm->trigger, calendar_notify, ico); +} + +static int +add_alarm (iCalObject *obj, time_t start, time_t end, void *closure) +{ + if (obj->aalarm.enabled) + try_add (obj, &obj->aalarm, start, end); + if (obj->dalarm.enabled) + try_add (obj, &obj->dalarm, start, end); + if (obj->palarm.enabled) + try_add (obj,&obj->palarm, start, end); + if (obj->malarm.enabled) + try_add (obj, &obj->malarm, start, end); + + return TRUE; +} + +#define max(a,b) ((a > b) ? a : b) + +void +ical_object_try_alarms (iCalObject *obj) +{ + GList *alarms, *p; + int ao, po, od, mo; + int max_o; + + ao = alarm_compute_offset (&obj->aalarm); + po = alarm_compute_offset (&obj->palarm); + od = alarm_compute_offset (&obj->dalarm); + mo = alarm_compute_offset (&obj->malarm); + + max_o = max (ao, max (po, max (od, mo))); + if (max_o == -1) + return; + + ical_object_generate_events (obj, calendar_day_begin, calendar_day_end + max_o, add_alarm, obj); +} + +void +calendar_add_alarms (Calendar *cal) +{ + time_t now = time (NULL); + GList *events = cal->events; + + for (; events; events=events->next) + ical_object_try_alarms (events->data); +} + void calendar_add_object (Calendar *cal, iCalObject *obj) { switch (obj->type){ case ICAL_EVENT: cal->events = g_list_prepend (cal->events, obj); + ical_object_try_alarms (obj); break; case ICAL_TODO: @@ -207,6 +269,7 @@ char * calendar_load (Calendar *cal, char *fname) { VObject *vcal; + time_t calendar_today; if (cal->filename){ g_warning ("Calendar load called again\n"); @@ -217,7 +280,11 @@ calendar_load (Calendar *cal, char *fname) vcal = Parse_MIME_FromFileName (fname); if (!vcal) return "Could not load the calendar"; - + + calendar_today = time (NULL); + calendar_day_begin = time_start_of_day (calendar_today); + calendar_day_end = time_end_of_day (calendar_today); + calendar_load_from_vobject (cal, vcal); cleanVObject (vcal); cleanStrTbl (); diff --git a/calendar/calendar.h b/calendar/calendar.h index 52cd2c90f6..91d0f3338c 100644 --- a/calendar/calendar.h +++ b/calendar/calendar.h @@ -54,6 +54,7 @@ GList *calendar_get_events_in_range (Calendar *cal, time_t start, time_t end /* Destroy the above list with this method */ void calendar_destroy_event_list (GList *l); +void calendar_notify (time_t, void *data); END_GNOME_DECLS #endif diff --git a/calendar/calobj.c b/calendar/calobj.c index 47d8c8a97e..624a179b05 100644 --- a/calendar/calobj.c +++ b/calendar/calobj.c @@ -1102,3 +1102,21 @@ ical_object_compute_end (iCalObject *ico) ico->recur->_enddate = 0; ical_object_generate_events (ico, ico->dtstart, 0, duration_callback, &count); } + +int +alarm_compute_offset (CalendarAlarm *a) +{ + if (!a->enabled) + return -1; + switch (a->units){ + case ALARM_MINUTES: + a->offset = a->count * 60; + break; + case ALARM_HOURS: + a->offset = a->count * 3600; + break; + case ALARM_DAYS: + a->offset = a->count * 24 * 3600; + } + return a->offset; +} diff --git a/calendar/calobj.h b/calendar/calobj.h index 26b0acf35b..60483c68ad 100644 --- a/calendar/calobj.h +++ b/calendar/calobj.h @@ -32,6 +32,10 @@ typedef struct { int count; enum AlarmUnit units; char *data; + + /* Does not get saved, internally used */ + time_t offset; + time_t trigger; /* Widgets */ void *w_count; /* A GtkEntry */ @@ -180,6 +184,9 @@ void ical_object_generate_events (iCalObject *ico, time_t start, time /* Computes the enddate field of the recurrence based on the duration */ void ical_object_compute_end (iCalObject *ico); +/* Returns the number of seconds configured to trigger the alarm in advance to an event */ +int alarm_compute_offset (CalendarAlarm *a); + END_GNOME_DECLS #endif diff --git a/calendar/eventedit.c b/calendar/eventedit.c index 3ea0f674ec..39a92c5480 100644 --- a/calendar/eventedit.c +++ b/calendar/eventedit.c @@ -1265,7 +1265,7 @@ get_exception_string (time_t t) { static char buf[256]; - strftime (buf, 256, "%a %b %d %Y", localtime (&t)); /* FIXME: how to i18n this? */ + strftime (buf, sizeof(buf), "%a %b %d %Y", localtime (&t)); /* FIXME: how to i18n this? */ return buf; } @@ -1483,7 +1483,7 @@ event_editor_new (GnomeCalendar *gcal, iCalObject *ical) ee = EVENT_EDITOR (retval); if (ical == 0){ - ical = ical_new ("Test Comment", user_name, "Test Summary"); + ical = ical_new ("", user_name, ""); ical->new = 1; } diff --git a/calendar/gnome-cal.c b/calendar/gnome-cal.c index 862c659d83..b851b65d44 100644 --- a/calendar/gnome-cal.c +++ b/calendar/gnome-cal.c @@ -6,6 +6,8 @@ */ #include <gnome.h> +#include <unistd.h> +#include <signal.h> #include "calendar.h" #include "gnome-cal.h" #include "gncal-full-day.h" @@ -54,11 +56,22 @@ day_view_range_activated (GncalFullDay *fullday, GnomeCalendar *gcal) } static void -setup_day_view (GnomeCalendar *gcal) +set_day_view_label (GnomeCalendar *gcal, time_t t) { - time_t a, b, now; + static char buf[256]; + + strftime (buf, sizeof (buf), "%a %b %d %Y", localtime (&t)); + gtk_label_set (GTK_LABEL (gcal->day_view_label), buf); +} + +static void +setup_day_view (GnomeCalendar *gcal, time_t now) +{ + GtkTable *t; + GtkWidget *sw; + + time_t a, b; - now = time (NULL); a = time_start_of_day (now); b = time_end_of_day (now); @@ -66,12 +79,29 @@ setup_day_view (GnomeCalendar *gcal) gtk_signal_connect (GTK_OBJECT (gcal->day_view), "range_activated", (GtkSignalFunc) day_view_range_activated, gcal); - gcal->day_view_container = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (gcal->day_view_container), + + t = (GtkTable *) gcal->day_view_container = gtk_table_new (0, 0, 0); + gtk_container_border_width (GTK_CONTAINER (t), 4); + gtk_table_set_row_spacings (t, 4); + gtk_table_set_col_spacings (t, 4); + + gcal->day_view_label = gtk_label_new (""); + set_day_view_label (gcal, now); + gtk_table_attach (t, gcal->day_view_label, 0, 1, 0, 1, + GTK_FILL | GTK_SHRINK, + GTK_FILL | GTK_SHRINK, + 0, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add (GTK_CONTAINER (gcal->day_view_container), gcal->day_view); - gtk_widget_show (gcal->day_view); + gtk_table_attach (t, sw, 0, 1, 1, 2, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + 0, 0); + gtk_container_add (GTK_CONTAINER (sw), gcal->day_view); + gtk_widget_show_all (GTK_WIDGET (t)); } static void @@ -86,7 +116,7 @@ setup_widgets (GnomeCalendar *gcal) gcal->year_view = gncal_year_view_new (gcal, now); gcal->task_view = tasks_create (gcal); - setup_day_view (gcal); + setup_day_view (gcal, now); gtk_notebook_append_page (GTK_NOTEBOOK (gcal->notebook), gcal->day_view_container, gtk_label_new (_("Day View"))); gtk_notebook_append_page (GTK_NOTEBOOK (gcal->notebook), gcal->week_view, gtk_label_new (_("Week View"))); @@ -129,6 +159,7 @@ gnome_calendar_goto (GnomeCalendar *gcal, time_t new_time) gncal_full_day_set_bounds (GNCAL_FULL_DAY (gcal->day_view), time_start_of_day (new_time), time_end_of_day (new_time)); + set_day_view_label (gcal, new_time); } else if (current == gcal->year_view) gncal_year_view_set (GNCAL_YEAR_VIEW (gcal->year_view), new_time); else @@ -157,7 +188,7 @@ gnome_calendar_direction (GnomeCalendar *gcal, int direction) void gnome_calendar_next (GnomeCalendar *gcal) { - gnome_calendar_direction (gcal, 1); +gnome_calendar_direction (gcal, 1); } void @@ -244,3 +275,111 @@ gnome_calendar_object_changed (GnomeCalendar *gcal, iCalObject *obj, int flags) gnome_calendar_update_all (gcal, obj, flags); } + +static int +max_open_files (void) +{ + static int files; + + if (files) + return files; + + files = sysconf (_SC_OPEN_MAX); + if (files != -1) + return files; +#ifdef OPEN_MAX + return files = OPEN_MAX; +#else + return files = 256; +#endif +} + +static void +execute (char *command, int close_standard) +{ + struct sigaction ignore, save_intr, save_quit; + int status = 0, i; + pid_t pid; + + ignore.sa_handler = SIG_IGN; + sigemptyset (&ignore.sa_mask); + ignore.sa_flags = 0; + + sigaction (SIGINT, &ignore, &save_intr); + sigaction (SIGQUIT, &ignore, &save_quit); + + if ((pid = fork ()) < 0){ + fprintf (stderr, "\n\nfork () = -1\n"); + return; + } + if (pid == 0){ + pid = fork (); + if (pid == 0){ + const int top = max_open_files (); + sigaction (SIGINT, &save_intr, NULL); + sigaction (SIGQUIT, &save_quit, NULL); + + for (i = (close_standard ? 0 : 3); i < 4096; i++) + close (i); + + /* FIXME: As an excercise to the reader, copy the + * code from mc to setup shell properly instead of + * /bin/sh. Yes, this comment is larger than a cut and paste. + */ + execl ("/bin/sh", "/bin/sh", "-c", command, (char *) 0); + + exit (127); + } else { + exit (127); + } + } + wait (&status); + sigaction (SIGINT, &save_intr, NULL); + sigaction (SIGQUIT, &save_quit, NULL); +} + +void +calendar_notify (time_t time, void *data) +{ + iCalObject *ico = data; + + if (ico->aalarm.enabled && ico->aalarm.trigger == time){ + printf ("bip\n"); + return; + } + + if (ico->palarm.enabled && ico->palarm.trigger == time){ + execute (ico->palarm.data, 0); + return; + } + + if (ico->malarm.enabled && ico->malarm.trigger == time){ + char *command; + time_t app = ico->malarm.trigger + ico->malarm.offset; + + command = g_copy_strings ("mail -s '", + _("Reminder of your appointment at "), + ctime (&app), "' '", + ico->malarm.data, "' ", + NULL); + execute (command, 1); + + g_free (command); + return; + } + + if (ico->dalarm.enabled && ico->dalarm.trigger == time){ + time_t app = ico->dalarm.trigger + ico->dalarm.offset; + GtkWidget *w; + char *msg; + + msg = g_copy_strings (_("Reminder of your appointment at "), + ctime (&app), "`", + ico->summary, "'", NULL); + w = gnome_message_box_new (msg, GNOME_MESSAGE_BOX_INFO, "Ok", NULL); + gtk_widget_show (w); + return; + } +} + + diff --git a/calendar/gnome-cal.h b/calendar/gnome-cal.h index e526d219d7..002aed07ae 100644 --- a/calendar/gnome-cal.h +++ b/calendar/gnome-cal.h @@ -28,6 +28,7 @@ typedef struct { GtkWidget *week_view; GtkWidget *day_view; GtkWidget *day_view_container; + GtkWidget *day_view_label; GtkWidget *year_view; GtkWidget *task_view; void *event_editor; diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am index b762dae475..9728a02d4b 100644 --- a/calendar/gui/Makefile.am +++ b/calendar/gui/Makefile.am @@ -9,6 +9,8 @@ INCLUDES = \ bin_PROGRAMS = gnomecal gnomecal_SOURCES = \ + alarm.c \ + alarm.h \ calendar.c \ calendar.h \ calobj.c \ diff --git a/calendar/gui/alarm-notify/alarm.c b/calendar/gui/alarm-notify/alarm.c new file mode 100644 index 0000000000..bb5c4c1b8d --- /dev/null +++ b/calendar/gui/alarm-notify/alarm.c @@ -0,0 +1,145 @@ +/* + * Alarm handling for the GNOME Calendar. + * + * (C) 1998 the Free Software Foundation + * + * Author: Miguel de Icaza (miguel@kernel.org) + */ +#include <config.h> +#include <time.h> +#include <gnome.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/time.h> +#include "alarm.h" + +/* The pipes used to notify about an alarm */ +int alarm_pipes [2]; + +/* The list of pending alarms */ +static GList *alarms; + +static void *head_alarm; + +typedef struct { + time_t activation_time; + AlarmFunction fn; + void *closure; +} AlarmRecord; + +/* + * SIGALRM handler. Notifies the callback about the alarm + */ +static void +alarm_activate () +{ + char c = 0; + + printf ("ALARMA!\n"); + write (alarm_pipes [1], &c, 1); +} + +static void +alarm_ready (void *closure, int fd, GdkInputCondition cond) +{ + AlarmRecord *ar = head_alarm; + char c; + + if (read (alarm_pipes [0], &c, 1) != 1) + return; + + if (ar == NULL){ + g_warning ("Empty events. This should not happen\n"); + return; + } + (*ar->fn)(ar->activation_time, ar->closure); + alarms = g_list_remove (alarms, head_alarm); + if (alarms) + head_alarm = alarms->data; + else + head_alarm = NULL; + g_free (ar); +} + +static int +alarm_compare_by_time (gpointer a, gpointer b) +{ + AlarmRecord *ara = a; + AlarmRecord *arb = b; + time_t diff; + + diff = ara->activation_time - arb->activation_time; + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; +} + +void +alarm_add (time_t alarm_time, AlarmFunction fn, void *closure) +{ + time_t now = time (NULL); + AlarmRecord *ar; + + /* If it already expired, do not add it */ + if (alarm_time < now) + return; + + ar = g_new0 (AlarmRecord, 1); + ar->activation_time = alarm_time; + ar->fn = fn; + ar->closure = closure; + + alarms = g_list_insert_sorted (alarms, ar, alarm_compare_by_time); + + /* If first alarm is not the previous first alarm, reschedule SIGALRM */ + if (head_alarm != alarms->data){ + struct itimerval itimer; + int v; + + /* Set the timer to disable upon activation */ + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = 0; + itimer.it_value.tv_sec = alarm_time - now; + itimer.it_value.tv_usec = 0; + v = setitimer (ITIMER_REAL, &itimer, NULL); + head_alarm = alarms->data; + } +} + +void +alarm_kill (void *closure_key) +{ + GList *p; + + for (p = alarms; p; p = p->next){ + AlarmRecord *ar = p->data; + + if (ar->closure == closure_key){ + alarms = g_list_remove (alarms, p->data); + if (alarms) + head_alarm = alarms->data; + else + head_alarm = NULL; + return; + } + } +} + +void +alarm_init (void) +{ + struct sigaction sa; + int flags; + + pipe (alarm_pipes); + + /* set non blocking mode */ + fcntl (alarm_pipes [0], F_GETFL, &flags); + fcntl (alarm_pipes [0], F_SETFL, flags | O_NONBLOCK); + gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, 0); + + /* Setup the signal handler */ + sa.sa_handler = alarm_activate; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction (SIGALRM, &sa, NULL); +} + diff --git a/calendar/gui/alarm-notify/alarm.h b/calendar/gui/alarm-notify/alarm.h new file mode 100644 index 0000000000..39f7281ce7 --- /dev/null +++ b/calendar/gui/alarm-notify/alarm.h @@ -0,0 +1,10 @@ +#ifndef ALARM_H +#define ALARM_H + +typedef void (*AlarmFunction)(time_t time, void *closuse); + +void alarm_init (void); +void alarm_add (time_t alarm_time, AlarmFunction fn, void *closure); +void alarm_kill (void *closure); + +#endif diff --git a/calendar/gui/alarm.c b/calendar/gui/alarm.c new file mode 100644 index 0000000000..bb5c4c1b8d --- /dev/null +++ b/calendar/gui/alarm.c @@ -0,0 +1,145 @@ +/* + * Alarm handling for the GNOME Calendar. + * + * (C) 1998 the Free Software Foundation + * + * Author: Miguel de Icaza (miguel@kernel.org) + */ +#include <config.h> +#include <time.h> +#include <gnome.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/time.h> +#include "alarm.h" + +/* The pipes used to notify about an alarm */ +int alarm_pipes [2]; + +/* The list of pending alarms */ +static GList *alarms; + +static void *head_alarm; + +typedef struct { + time_t activation_time; + AlarmFunction fn; + void *closure; +} AlarmRecord; + +/* + * SIGALRM handler. Notifies the callback about the alarm + */ +static void +alarm_activate () +{ + char c = 0; + + printf ("ALARMA!\n"); + write (alarm_pipes [1], &c, 1); +} + +static void +alarm_ready (void *closure, int fd, GdkInputCondition cond) +{ + AlarmRecord *ar = head_alarm; + char c; + + if (read (alarm_pipes [0], &c, 1) != 1) + return; + + if (ar == NULL){ + g_warning ("Empty events. This should not happen\n"); + return; + } + (*ar->fn)(ar->activation_time, ar->closure); + alarms = g_list_remove (alarms, head_alarm); + if (alarms) + head_alarm = alarms->data; + else + head_alarm = NULL; + g_free (ar); +} + +static int +alarm_compare_by_time (gpointer a, gpointer b) +{ + AlarmRecord *ara = a; + AlarmRecord *arb = b; + time_t diff; + + diff = ara->activation_time - arb->activation_time; + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; +} + +void +alarm_add (time_t alarm_time, AlarmFunction fn, void *closure) +{ + time_t now = time (NULL); + AlarmRecord *ar; + + /* If it already expired, do not add it */ + if (alarm_time < now) + return; + + ar = g_new0 (AlarmRecord, 1); + ar->activation_time = alarm_time; + ar->fn = fn; + ar->closure = closure; + + alarms = g_list_insert_sorted (alarms, ar, alarm_compare_by_time); + + /* If first alarm is not the previous first alarm, reschedule SIGALRM */ + if (head_alarm != alarms->data){ + struct itimerval itimer; + int v; + + /* Set the timer to disable upon activation */ + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = 0; + itimer.it_value.tv_sec = alarm_time - now; + itimer.it_value.tv_usec = 0; + v = setitimer (ITIMER_REAL, &itimer, NULL); + head_alarm = alarms->data; + } +} + +void +alarm_kill (void *closure_key) +{ + GList *p; + + for (p = alarms; p; p = p->next){ + AlarmRecord *ar = p->data; + + if (ar->closure == closure_key){ + alarms = g_list_remove (alarms, p->data); + if (alarms) + head_alarm = alarms->data; + else + head_alarm = NULL; + return; + } + } +} + +void +alarm_init (void) +{ + struct sigaction sa; + int flags; + + pipe (alarm_pipes); + + /* set non blocking mode */ + fcntl (alarm_pipes [0], F_GETFL, &flags); + fcntl (alarm_pipes [0], F_SETFL, flags | O_NONBLOCK); + gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, 0); + + /* Setup the signal handler */ + sa.sa_handler = alarm_activate; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction (SIGALRM, &sa, NULL); +} + diff --git a/calendar/gui/alarm.h b/calendar/gui/alarm.h new file mode 100644 index 0000000000..39f7281ce7 --- /dev/null +++ b/calendar/gui/alarm.h @@ -0,0 +1,10 @@ +#ifndef ALARM_H +#define ALARM_H + +typedef void (*AlarmFunction)(time_t time, void *closuse); + +void alarm_init (void); +void alarm_add (time_t alarm_time, AlarmFunction fn, void *closure); +void alarm_kill (void *closure); + +#endif diff --git a/calendar/gui/calendar.c b/calendar/gui/calendar.c index be45bb04bb..28f37f78eb 100644 --- a/calendar/gui/calendar.c +++ b/calendar/gui/calendar.c @@ -18,6 +18,9 @@ #include "timeutil.h" #include "versit/vcc.h" +/* Our day range */ +time_t calendar_day_begin, calendar_day_end; + Calendar * calendar_new (char *title) { @@ -29,12 +32,71 @@ calendar_new (char *title) return cal; } +static void +try_add (iCalObject *ico, CalendarAlarm *alarm, time_t start, time_t end) +{ + alarm->trigger = start-alarm->offset; + + if (alarm->trigger < calendar_day_begin) + return; + if (alarm->trigger > calendar_day_end) + return; + alarm_add (alarm->trigger, calendar_notify, ico); +} + +static int +add_alarm (iCalObject *obj, time_t start, time_t end, void *closure) +{ + if (obj->aalarm.enabled) + try_add (obj, &obj->aalarm, start, end); + if (obj->dalarm.enabled) + try_add (obj, &obj->dalarm, start, end); + if (obj->palarm.enabled) + try_add (obj,&obj->palarm, start, end); + if (obj->malarm.enabled) + try_add (obj, &obj->malarm, start, end); + + return TRUE; +} + +#define max(a,b) ((a > b) ? a : b) + +void +ical_object_try_alarms (iCalObject *obj) +{ + GList *alarms, *p; + int ao, po, od, mo; + int max_o; + + ao = alarm_compute_offset (&obj->aalarm); + po = alarm_compute_offset (&obj->palarm); + od = alarm_compute_offset (&obj->dalarm); + mo = alarm_compute_offset (&obj->malarm); + + max_o = max (ao, max (po, max (od, mo))); + if (max_o == -1) + return; + + ical_object_generate_events (obj, calendar_day_begin, calendar_day_end + max_o, add_alarm, obj); +} + +void +calendar_add_alarms (Calendar *cal) +{ + time_t now = time (NULL); + GList *events = cal->events; + + for (; events; events=events->next) + ical_object_try_alarms (events->data); +} + void calendar_add_object (Calendar *cal, iCalObject *obj) { switch (obj->type){ case ICAL_EVENT: cal->events = g_list_prepend (cal->events, obj); + ical_object_try_alarms (obj); break; case ICAL_TODO: @@ -207,6 +269,7 @@ char * calendar_load (Calendar *cal, char *fname) { VObject *vcal; + time_t calendar_today; if (cal->filename){ g_warning ("Calendar load called again\n"); @@ -217,7 +280,11 @@ calendar_load (Calendar *cal, char *fname) vcal = Parse_MIME_FromFileName (fname); if (!vcal) return "Could not load the calendar"; - + + calendar_today = time (NULL); + calendar_day_begin = time_start_of_day (calendar_today); + calendar_day_end = time_end_of_day (calendar_today); + calendar_load_from_vobject (cal, vcal); cleanVObject (vcal); cleanStrTbl (); diff --git a/calendar/gui/calendar.h b/calendar/gui/calendar.h index 52cd2c90f6..91d0f3338c 100644 --- a/calendar/gui/calendar.h +++ b/calendar/gui/calendar.h @@ -54,6 +54,7 @@ GList *calendar_get_events_in_range (Calendar *cal, time_t start, time_t end /* Destroy the above list with this method */ void calendar_destroy_event_list (GList *l); +void calendar_notify (time_t, void *data); END_GNOME_DECLS #endif diff --git a/calendar/gui/eventedit.c b/calendar/gui/eventedit.c index 3ea0f674ec..39a92c5480 100644 --- a/calendar/gui/eventedit.c +++ b/calendar/gui/eventedit.c @@ -1265,7 +1265,7 @@ get_exception_string (time_t t) { static char buf[256]; - strftime (buf, 256, "%a %b %d %Y", localtime (&t)); /* FIXME: how to i18n this? */ + strftime (buf, sizeof(buf), "%a %b %d %Y", localtime (&t)); /* FIXME: how to i18n this? */ return buf; } @@ -1483,7 +1483,7 @@ event_editor_new (GnomeCalendar *gcal, iCalObject *ical) ee = EVENT_EDITOR (retval); if (ical == 0){ - ical = ical_new ("Test Comment", user_name, "Test Summary"); + ical = ical_new ("", user_name, ""); ical->new = 1; } diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index 862c659d83..b851b65d44 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -6,6 +6,8 @@ */ #include <gnome.h> +#include <unistd.h> +#include <signal.h> #include "calendar.h" #include "gnome-cal.h" #include "gncal-full-day.h" @@ -54,11 +56,22 @@ day_view_range_activated (GncalFullDay *fullday, GnomeCalendar *gcal) } static void -setup_day_view (GnomeCalendar *gcal) +set_day_view_label (GnomeCalendar *gcal, time_t t) { - time_t a, b, now; + static char buf[256]; + + strftime (buf, sizeof (buf), "%a %b %d %Y", localtime (&t)); + gtk_label_set (GTK_LABEL (gcal->day_view_label), buf); +} + +static void +setup_day_view (GnomeCalendar *gcal, time_t now) +{ + GtkTable *t; + GtkWidget *sw; + + time_t a, b; - now = time (NULL); a = time_start_of_day (now); b = time_end_of_day (now); @@ -66,12 +79,29 @@ setup_day_view (GnomeCalendar *gcal) gtk_signal_connect (GTK_OBJECT (gcal->day_view), "range_activated", (GtkSignalFunc) day_view_range_activated, gcal); - gcal->day_view_container = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (gcal->day_view_container), + + t = (GtkTable *) gcal->day_view_container = gtk_table_new (0, 0, 0); + gtk_container_border_width (GTK_CONTAINER (t), 4); + gtk_table_set_row_spacings (t, 4); + gtk_table_set_col_spacings (t, 4); + + gcal->day_view_label = gtk_label_new (""); + set_day_view_label (gcal, now); + gtk_table_attach (t, gcal->day_view_label, 0, 1, 0, 1, + GTK_FILL | GTK_SHRINK, + GTK_FILL | GTK_SHRINK, + 0, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add (GTK_CONTAINER (gcal->day_view_container), gcal->day_view); - gtk_widget_show (gcal->day_view); + gtk_table_attach (t, sw, 0, 1, 1, 2, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + 0, 0); + gtk_container_add (GTK_CONTAINER (sw), gcal->day_view); + gtk_widget_show_all (GTK_WIDGET (t)); } static void @@ -86,7 +116,7 @@ setup_widgets (GnomeCalendar *gcal) gcal->year_view = gncal_year_view_new (gcal, now); gcal->task_view = tasks_create (gcal); - setup_day_view (gcal); + setup_day_view (gcal, now); gtk_notebook_append_page (GTK_NOTEBOOK (gcal->notebook), gcal->day_view_container, gtk_label_new (_("Day View"))); gtk_notebook_append_page (GTK_NOTEBOOK (gcal->notebook), gcal->week_view, gtk_label_new (_("Week View"))); @@ -129,6 +159,7 @@ gnome_calendar_goto (GnomeCalendar *gcal, time_t new_time) gncal_full_day_set_bounds (GNCAL_FULL_DAY (gcal->day_view), time_start_of_day (new_time), time_end_of_day (new_time)); + set_day_view_label (gcal, new_time); } else if (current == gcal->year_view) gncal_year_view_set (GNCAL_YEAR_VIEW (gcal->year_view), new_time); else @@ -157,7 +188,7 @@ gnome_calendar_direction (GnomeCalendar *gcal, int direction) void gnome_calendar_next (GnomeCalendar *gcal) { - gnome_calendar_direction (gcal, 1); +gnome_calendar_direction (gcal, 1); } void @@ -244,3 +275,111 @@ gnome_calendar_object_changed (GnomeCalendar *gcal, iCalObject *obj, int flags) gnome_calendar_update_all (gcal, obj, flags); } + +static int +max_open_files (void) +{ + static int files; + + if (files) + return files; + + files = sysconf (_SC_OPEN_MAX); + if (files != -1) + return files; +#ifdef OPEN_MAX + return files = OPEN_MAX; +#else + return files = 256; +#endif +} + +static void +execute (char *command, int close_standard) +{ + struct sigaction ignore, save_intr, save_quit; + int status = 0, i; + pid_t pid; + + ignore.sa_handler = SIG_IGN; + sigemptyset (&ignore.sa_mask); + ignore.sa_flags = 0; + + sigaction (SIGINT, &ignore, &save_intr); + sigaction (SIGQUIT, &ignore, &save_quit); + + if ((pid = fork ()) < 0){ + fprintf (stderr, "\n\nfork () = -1\n"); + return; + } + if (pid == 0){ + pid = fork (); + if (pid == 0){ + const int top = max_open_files (); + sigaction (SIGINT, &save_intr, NULL); + sigaction (SIGQUIT, &save_quit, NULL); + + for (i = (close_standard ? 0 : 3); i < 4096; i++) + close (i); + + /* FIXME: As an excercise to the reader, copy the + * code from mc to setup shell properly instead of + * /bin/sh. Yes, this comment is larger than a cut and paste. + */ + execl ("/bin/sh", "/bin/sh", "-c", command, (char *) 0); + + exit (127); + } else { + exit (127); + } + } + wait (&status); + sigaction (SIGINT, &save_intr, NULL); + sigaction (SIGQUIT, &save_quit, NULL); +} + +void +calendar_notify (time_t time, void *data) +{ + iCalObject *ico = data; + + if (ico->aalarm.enabled && ico->aalarm.trigger == time){ + printf ("bip\n"); + return; + } + + if (ico->palarm.enabled && ico->palarm.trigger == time){ + execute (ico->palarm.data, 0); + return; + } + + if (ico->malarm.enabled && ico->malarm.trigger == time){ + char *command; + time_t app = ico->malarm.trigger + ico->malarm.offset; + + command = g_copy_strings ("mail -s '", + _("Reminder of your appointment at "), + ctime (&app), "' '", + ico->malarm.data, "' ", + NULL); + execute (command, 1); + + g_free (command); + return; + } + + if (ico->dalarm.enabled && ico->dalarm.trigger == time){ + time_t app = ico->dalarm.trigger + ico->dalarm.offset; + GtkWidget *w; + char *msg; + + msg = g_copy_strings (_("Reminder of your appointment at "), + ctime (&app), "`", + ico->summary, "'", NULL); + w = gnome_message_box_new (msg, GNOME_MESSAGE_BOX_INFO, "Ok", NULL); + gtk_widget_show (w); + return; + } +} + + diff --git a/calendar/gui/gnome-cal.h b/calendar/gui/gnome-cal.h index e526d219d7..002aed07ae 100644 --- a/calendar/gui/gnome-cal.h +++ b/calendar/gui/gnome-cal.h @@ -28,6 +28,7 @@ typedef struct { GtkWidget *week_view; GtkWidget *day_view; GtkWidget *day_view_container; + GtkWidget *day_view_label; GtkWidget *year_view; GtkWidget *task_view; void *event_editor; diff --git a/calendar/gui/main.c b/calendar/gui/main.c index 8249763ff0..ff4fc370a9 100644 --- a/calendar/gui/main.c +++ b/calendar/gui/main.c @@ -11,6 +11,7 @@ #include <gnome.h> #include <pwd.h> #include <sys/types.h> + #include "calendar.h" #include "eventedit.h" #include "gnome-cal.h" @@ -96,6 +97,8 @@ init_calendar (void) gnome_config_pop_prefix (); } +void save_calendar_cmd (GtkWidget *widget, void *data); + void about_calendar_cmd (GtkWidget *widget, void *data) { @@ -125,30 +128,34 @@ display_objedit (GtkWidget *widget, GnomeCalendar *gcal) } void -quit_cmd (GtkWidget *widget, GnomeCalendar *gcal) -{ - /* FIXME: check all of the calendars for their state (modified) */ - - gtk_main_quit (); -} - -void close_cmd (GtkWidget *widget, GnomeCalendar *gcal) { if (gcal->cal->modified){ - gnome_message_box_new (_("The calendar has unsaved changes, Save them?"), - GNOME_MESSAGE_BOX_WARNING, - "Yes", "No"); + if (!gcal->cal->filename) + save_calendar_cmd (widget, gcal); + else + calendar_save (gcal->cal, gcal->cal->filename); } - gtk_widget_destroy (widget); + gtk_widget_destroy (GTK_WIDGET (gcal)); active_calendars--; - + all_calendars = g_list_remove (all_calendars, gcal); + if (active_calendars == 0) gtk_main_quit (); } void +quit_cmd (GtkWidget *widget, GnomeCalendar *gcal) +{ + while (all_calendars){ + GnomeCalendar *cal = GNOME_CALENDAR (all_calendars->data); + + close_cmd (GTK_WIDGET (cal), cal); + } +} + +void previous_clicked (GtkWidget *widget, GnomeCalendar *gcal) { gnome_calendar_previous (gcal); @@ -307,7 +314,6 @@ new_calendar (char *full_name, char *calendar_file) printf ("Trying to load %s\n", calendar_file); gnome_calendar_load (GNOME_CALENDAR (toplevel), calendar_file); } - active_calendars++; all_calendars = g_list_prepend (all_calendars, toplevel); gtk_widget_show (toplevel); @@ -320,12 +326,12 @@ main(int argc, char *argv[]) argp_program_version = VERSION; - /* Initialise the i18n stuff */ bindtextdomain(PACKAGE, GNOMELOCALEDIR); textdomain(PACKAGE); gnome_init ("gncal", NULL, argc, argv, 0, NULL); + alarm_init (); init_calendar (); new_calendar (full_name, user_calendar_file); diff --git a/calendar/main.c b/calendar/main.c index 8249763ff0..ff4fc370a9 100644 --- a/calendar/main.c +++ b/calendar/main.c @@ -11,6 +11,7 @@ #include <gnome.h> #include <pwd.h> #include <sys/types.h> + #include "calendar.h" #include "eventedit.h" #include "gnome-cal.h" @@ -96,6 +97,8 @@ init_calendar (void) gnome_config_pop_prefix (); } +void save_calendar_cmd (GtkWidget *widget, void *data); + void about_calendar_cmd (GtkWidget *widget, void *data) { @@ -125,30 +128,34 @@ display_objedit (GtkWidget *widget, GnomeCalendar *gcal) } void -quit_cmd (GtkWidget *widget, GnomeCalendar *gcal) -{ - /* FIXME: check all of the calendars for their state (modified) */ - - gtk_main_quit (); -} - -void close_cmd (GtkWidget *widget, GnomeCalendar *gcal) { if (gcal->cal->modified){ - gnome_message_box_new (_("The calendar has unsaved changes, Save them?"), - GNOME_MESSAGE_BOX_WARNING, - "Yes", "No"); + if (!gcal->cal->filename) + save_calendar_cmd (widget, gcal); + else + calendar_save (gcal->cal, gcal->cal->filename); } - gtk_widget_destroy (widget); + gtk_widget_destroy (GTK_WIDGET (gcal)); active_calendars--; - + all_calendars = g_list_remove (all_calendars, gcal); + if (active_calendars == 0) gtk_main_quit (); } void +quit_cmd (GtkWidget *widget, GnomeCalendar *gcal) +{ + while (all_calendars){ + GnomeCalendar *cal = GNOME_CALENDAR (all_calendars->data); + + close_cmd (GTK_WIDGET (cal), cal); + } +} + +void previous_clicked (GtkWidget *widget, GnomeCalendar *gcal) { gnome_calendar_previous (gcal); @@ -307,7 +314,6 @@ new_calendar (char *full_name, char *calendar_file) printf ("Trying to load %s\n", calendar_file); gnome_calendar_load (GNOME_CALENDAR (toplevel), calendar_file); } - active_calendars++; all_calendars = g_list_prepend (all_calendars, toplevel); gtk_widget_show (toplevel); @@ -320,12 +326,12 @@ main(int argc, char *argv[]) argp_program_version = VERSION; - /* Initialise the i18n stuff */ bindtextdomain(PACKAGE, GNOMELOCALEDIR); textdomain(PACKAGE); gnome_init ("gncal", NULL, argc, argv, 0, NULL); + alarm_init (); init_calendar (); new_calendar (full_name, user_calendar_file); diff --git a/calendar/pcs/calobj.c b/calendar/pcs/calobj.c index 47d8c8a97e..624a179b05 100644 --- a/calendar/pcs/calobj.c +++ b/calendar/pcs/calobj.c @@ -1102,3 +1102,21 @@ ical_object_compute_end (iCalObject *ico) ico->recur->_enddate = 0; ical_object_generate_events (ico, ico->dtstart, 0, duration_callback, &count); } + +int +alarm_compute_offset (CalendarAlarm *a) +{ + if (!a->enabled) + return -1; + switch (a->units){ + case ALARM_MINUTES: + a->offset = a->count * 60; + break; + case ALARM_HOURS: + a->offset = a->count * 3600; + break; + case ALARM_DAYS: + a->offset = a->count * 24 * 3600; + } + return a->offset; +} diff --git a/calendar/pcs/calobj.h b/calendar/pcs/calobj.h index 26b0acf35b..60483c68ad 100644 --- a/calendar/pcs/calobj.h +++ b/calendar/pcs/calobj.h @@ -32,6 +32,10 @@ typedef struct { int count; enum AlarmUnit units; char *data; + + /* Does not get saved, internally used */ + time_t offset; + time_t trigger; /* Widgets */ void *w_count; /* A GtkEntry */ @@ -180,6 +184,9 @@ void ical_object_generate_events (iCalObject *ico, time_t start, time /* Computes the enddate field of the recurrence based on the duration */ void ical_object_compute_end (iCalObject *ico); +/* Returns the number of seconds configured to trigger the alarm in advance to an event */ +int alarm_compute_offset (CalendarAlarm *a); + END_GNOME_DECLS #endif diff --git a/calendar/timeutil.c b/calendar/timeutil.c index 3e912a0fa3..09123b3202 100644 --- a/calendar/timeutil.c +++ b/calendar/timeutil.c @@ -57,7 +57,7 @@ isodate_from_time_t (time_t t) static char isotime [40]; tm = localtime (&t); - strftime (isotime, sizeof (isotime)-1, "%Y%m%dT%H%M%sZ", tm); + strftime (isotime, sizeof (isotime)-1, "%Y%m%dT%H%M%SZ", tm); return isotime; } |