"Fossies" - the Fresh Open Source Software Archive

Member "jpilot-2_0_1/alarms.c" (3 Apr 2021, 31236 Bytes) of package /linux/privat/jpilot-2_0_1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "alarms.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.8.2_vs_2_0_1.

    1 /*******************************************************************************
    2  * alarms.c
    3  * A module of J-Pilot http://jpilot.org
    4  *
    5  * Copyright (C) 2000-2014 by Judd Montgomery
    6  *
    7  * This program is free software; you can redistribute it and/or modify
    8  * it under the terms of the GNU General Public License as published by
    9  * the Free Software Foundation; version 2 of the License.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License
   17  * along with this program; if not, write to the Free Software
   18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   19  ******************************************************************************/
   20 /*
   21  * The PalmOS datebook will alarm on private records even when they are hidden
   22  * and they show up on the screen.  Right, or wrong, who knows.
   23  * I will do the same.
   24  *
   25  * Throughout the code, event_time is the time of the event
   26  * alarm_time is the event_time - advance
   27  * remind_time is the time a window is to be popped up (it may be postponed)
   28  */
   29 
   30 /********************************* Includes ***********************************/
   31 #include "config.h"
   32 #include <gtk/gtk.h>
   33 #include <time.h>
   34 #include <stdio.h>
   35 #include <stdlib.h>
   36 #include <string.h>
   37 
   38 #include <pi-calendar.h>
   39 
   40 #include "alarms.h"
   41 #include "i18n.h"
   42 #include "utils.h"
   43 #include "calendar.h"
   44 #include "log.h"
   45 #include "prefs.h"
   46 
   47 /********************************* Constants **********************************/
   48 /* This is how often to check for alarms in seconds */
   49 /* Every call takes CPU time(not much), so you may want it to be greater */
   50 #define ALARM_INTERVAL 10
   51 
   52 /* Constants used in converting to seconds */
   53 #define MIN_IN_SECS 60
   54 #define HR_IN_SECS  3600
   55 #define DAY_IN_SECS 86400
   56 
   57 #define PREV_ALARM_MASK 1
   58 #define NEXT_ALARM_MASK 2
   59 
   60 /* Uncomment for verbose debugging of the alarm code */
   61 /* #define ALARMS_DEBUG */
   62 
   63 /******************************* Global vars **********************************/
   64 /* main jpilot window */
   65 extern GtkWidget *window;
   66 
   67 static struct jp_alarms *alarm_list=NULL;
   68 static struct jp_alarms *Plast_alarm_list=NULL;
   69 static struct jp_alarms *next_alarm=NULL;
   70 
   71 static int glob_skip_all_alarms;
   72 static int total_alarm_windows;
   73 
   74 /****************************** Prototypes ************************************/
   75 typedef enum {
   76    ALARM_NONE = 0,
   77    ALARM_NEW,
   78    ALARM_MISSED,
   79    ALARM_POSTPONED
   80 } AlarmType;
   81 
   82 struct jp_alarms {
   83    unsigned int unique_id;
   84    AlarmType type;
   85    time_t event_time;
   86    time_t alarm_advance;
   87    struct jp_alarms *next;
   88 };
   89 
   90 struct alarm_dialog_data {
   91    unsigned int unique_id;
   92    time_t remind_time;
   93    GtkWidget *remind_entry;
   94    GtkWidget *radio1;
   95    GtkWidget *radio2;
   96    int button_hit;
   97 };
   98 
   99 static void alarms_add_to_list(unsigned int unique_id,
  100                                AlarmType type,
  101                                time_t alarm_time,
  102                                time_t alarm_advance);
  103 
  104 /****************************** Main Code *************************************/
  105 /* Alarm GUI */
  106 
  107 /* Start of Dialog window code */
  108 static void cb_dialog_button(GtkWidget *widget, gpointer data)
  109 {
  110    struct alarm_dialog_data *Pdata;
  111    GtkWidget *w;
  112 
  113    w = gtk_widget_get_toplevel(widget);
  114    Pdata =  g_object_get_data(G_OBJECT(w), "alarm");
  115    if (Pdata) {
  116       Pdata->button_hit = GPOINTER_TO_INT(data);
  117    }
  118    gtk_widget_destroy(w);
  119 }
  120 
  121 static gboolean cb_destroy_dialog(GtkWidget *widget)
  122 {
  123    struct alarm_dialog_data *Pdata;
  124    time_t ltime;
  125    time_t advance;
  126    time_t remind;
  127 
  128    total_alarm_windows--;
  129 #ifdef ALARMS_DEBUG
  130    printf("total_alarm_windows=%d\n",total_alarm_windows);
  131 #endif
  132    Pdata =  g_object_get_data(G_OBJECT(widget), "alarm");
  133    if (!Pdata) {
  134       return FALSE;
  135    }
  136    if (Pdata->button_hit==DIALOG_SAID_2) {
  137       remind = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(Pdata->remind_entry));
  138       jp_logf(JP_LOG_DEBUG, "remind = [%d]\n", remind);
  139       set_pref(PREF_REMIND_IN, remind, NULL, TRUE);
  140       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(Pdata->radio1))) {
  141          set_pref(PREF_REMIND_UNITS, 0, NULL, TRUE);
  142          remind *= MIN_IN_SECS;
  143       } else {
  144          set_pref(PREF_REMIND_UNITS, 1, NULL, TRUE);
  145          remind *= HR_IN_SECS;
  146       }
  147       time(&ltime);
  148       localtime(&ltime);
  149       advance = -(ltime + remind - Pdata->remind_time);
  150       alarms_add_to_list(Pdata->unique_id,
  151                          ALARM_POSTPONED,
  152                          Pdata->remind_time,
  153                          advance);
  154    }
  155    free(Pdata);
  156 
  157    /* Done with cleanup.  Let GTK continue with removing widgets */
  158    return FALSE;
  159 }
  160 
  161 static int dialog_alarm(char *title, char *reason,
  162                         char *time_str, char *desc_str, char *note_str,
  163                         unsigned int unique_id,
  164                         time_t remind_time)
  165 {
  166    GSList *group;
  167    GtkWidget *button, *label;
  168    GtkWidget *hbox1, *vbox1;
  169    GtkWidget *vbox_temp;
  170    GtkWidget *alarm_dialog;
  171    GtkWidget *remind_entry;
  172    GtkWidget *radio1;
  173    GtkWidget *radio2;
  174    struct alarm_dialog_data *Pdata;
  175    long pref_units;
  176    long pref_entry;
  177    GtkWidget *image;
  178    char *markup;
  179 
  180    /* Prevent alarms from going crazy and using all resources */
  181    if (total_alarm_windows > 20) {
  182       return EXIT_FAILURE;
  183    }
  184    total_alarm_windows++;
  185 #ifdef ALARMS_DEBUG
  186    printf("total_alarm_windows=%d\n",total_alarm_windows);
  187 #endif
  188    alarm_dialog = gtk_widget_new(GTK_TYPE_WINDOW,
  189                                  "type", GTK_WINDOW_TOPLEVEL,
  190                                  "title", title,
  191                                  NULL);
  192 
  193    g_signal_connect(G_OBJECT(alarm_dialog), "destroy",
  194                       G_CALLBACK(cb_destroy_dialog), alarm_dialog);
  195 
  196    gtk_window_set_transient_for(GTK_WINDOW(alarm_dialog), GTK_WINDOW(window));
  197    gtk_window_stick(GTK_WINDOW(alarm_dialog));
  198 
  199    vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
  200    gtk_container_add(GTK_CONTAINER(alarm_dialog), vbox1);
  201 
  202    hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
  203    gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 0);
  204 
  205    image = gtk_image_new_from_icon_name("dialog-information", GTK_ICON_SIZE_DIALOG);
  206    gtk_box_pack_start(GTK_BOX(hbox1), image, FALSE, FALSE, 12);
  207 
  208    /* Label */
  209    label = gtk_label_new("");
  210    if (note_str[0] == '\0') {
  211       markup = g_markup_printf_escaped("<b><big>%s</big></b>\n\n%s\n\n%s",
  212                desc_str, reason, time_str);
  213    } else {
  214       markup = g_markup_printf_escaped("<b><big>%s</big></b>\n\n%s\n\n%s\n\n%s",
  215                desc_str, reason, time_str, note_str);
  216    }
  217    gtk_label_set_markup(GTK_LABEL(label), markup);
  218    g_free(markup);
  219    gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
  220    gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 6);
  221 
  222    /* remind delay */
  223    hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  224    remind_entry = gtk_spin_button_new_with_range(0, 59, 1);
  225    gtk_box_pack_start(GTK_BOX(hbox1), remind_entry, FALSE, FALSE, 2);
  226 
  227    vbox_temp = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  228    gtk_box_pack_start(GTK_BOX(hbox1), vbox_temp, FALSE, TRUE, 4);
  229 
  230    radio1 = gtk_radio_button_new_with_label(NULL, _("Minutes"));
  231    group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio1));
  232    radio2 = gtk_radio_button_new_with_label(group, _("Hours"));
  233    gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio2));
  234 
  235    gtk_box_pack_start(GTK_BOX(vbox_temp), radio1, TRUE, TRUE, 0);
  236    gtk_box_pack_start(GTK_BOX(vbox_temp), radio2, TRUE, TRUE, 0);
  237 
  238    gtk_box_pack_start(GTK_BOX(vbox1), hbox1, TRUE, TRUE, 2);
  239 
  240    get_pref(PREF_REMIND_IN, &pref_entry, NULL);
  241    gtk_spin_button_set_value(GTK_SPIN_BUTTON(remind_entry), pref_entry);
  242 
  243    get_pref(PREF_REMIND_UNITS, &pref_units, NULL);
  244    if (pref_units) {
  245       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio2), TRUE);
  246    } else {
  247       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio1), TRUE);
  248    }
  249 
  250    /* Buttons */
  251    gtk_container_set_border_width(GTK_CONTAINER(hbox1), 12);
  252 
  253    button = gtk_button_new_with_label(_("Remind me"));
  254    g_signal_connect(G_OBJECT(button), "clicked",
  255                       G_CALLBACK(cb_dialog_button),
  256                       GINT_TO_POINTER(DIALOG_SAID_2));
  257    gtk_box_pack_start(GTK_BOX(hbox1), button, TRUE, TRUE, 4);
  258 
  259    button = gtk_button_new_with_label("Close");
  260    g_signal_connect(G_OBJECT(button), "clicked",
  261                       G_CALLBACK(cb_dialog_button),
  262                       GINT_TO_POINTER(DIALOG_SAID_1));
  263    gtk_box_pack_start(GTK_BOX(hbox1), button, TRUE, TRUE, 4);
  264 
  265    Pdata = malloc(sizeof(struct alarm_dialog_data));
  266    if (Pdata) {
  267       Pdata->unique_id = unique_id;
  268       Pdata->remind_time = remind_time;
  269       /* Set the default button pressed to OK */
  270       Pdata->button_hit = DIALOG_SAID_1;
  271       Pdata->remind_entry=remind_entry;
  272       Pdata->radio1=radio1;
  273       Pdata->radio2=radio2;
  274    }
  275     g_object_set_data(G_OBJECT(alarm_dialog), "alarm", Pdata);
  276 
  277    gtk_widget_show_all(alarm_dialog);
  278 
  279    return EXIT_SUCCESS;
  280 }
  281 /* End Alarm GUI */
  282 
  283 static time_t tm_copy_with_dst_adj(struct tm *dest, struct tm *src)
  284 {
  285    memcpy(dest, src, sizeof(struct tm));
  286    dest->tm_isdst=-1;
  287    return mktime(dest);
  288 }
  289 
  290 #ifdef ALARMS_DEBUG
  291 static const char *print_date(const time_t t1)
  292 {
  293    struct tm *Pnow;
  294    static char str[100];
  295 
  296    Pnow = localtime(&t1);
  297    strftime(str, sizeof(str), "%B %d, %Y %H:%M:%S", Pnow);
  298    return str;
  299 }
  300 static const char *print_type(AlarmType type)
  301 {
  302    switch (type) {
  303     case ALARM_NONE:
  304       return "ALARM_NONE";
  305     case ALARM_NEW:
  306       return "ALARM_NEW";
  307     case ALARM_MISSED:
  308       return "ALARM_MISSED";
  309     case ALARM_POSTPONED:
  310       return "ALARM_POSTPONED";
  311     default:
  312       return "? ALARM_UNKNOWN";
  313    }
  314 }
  315 #endif
  316 
  317 static void alarms_add_to_list(unsigned int unique_id,
  318                         AlarmType type,
  319                         time_t event_time,
  320                         time_t alarm_advance)
  321 {
  322    struct jp_alarms *temp_alarm;
  323 
  324 #ifdef ALARMS_DEBUG
  325    printf("alarms_add_to_list()\n");
  326 #endif
  327 
  328    temp_alarm = malloc(sizeof(struct jp_alarms));
  329    if (!temp_alarm) {
  330       jp_logf(JP_LOG_WARN, "alarms_add_to_list: %s\n", _("Out of memory"));
  331       return;
  332    }
  333    temp_alarm->unique_id = unique_id;
  334    temp_alarm->type = type;
  335    temp_alarm->event_time = event_time;
  336    temp_alarm->alarm_advance = alarm_advance;
  337    temp_alarm->next = NULL;
  338    if (Plast_alarm_list) {
  339       Plast_alarm_list->next=temp_alarm;
  340       Plast_alarm_list=temp_alarm;
  341    } else {
  342       alarm_list=Plast_alarm_list=temp_alarm;
  343    }
  344    Plast_alarm_list=temp_alarm;
  345 }
  346 
  347 static void alarms_remove_from_to_list(unsigned int unique_id)
  348 {
  349    struct jp_alarms *temp_alarm, *prev_alarm, *next_alarm;
  350 
  351 #ifdef ALARMS_DEBUG
  352    printf("remove from list(%d)\n", unique_id);
  353 #endif
  354    for(prev_alarm=NULL, temp_alarm=alarm_list;
  355        temp_alarm;
  356        temp_alarm=next_alarm) {
  357       if (temp_alarm->unique_id==unique_id) {
  358          /* Tail of list? */
  359          if (temp_alarm->next==NULL) {
  360             Plast_alarm_list=prev_alarm;
  361          }
  362          /* Last of list? */
  363          if (Plast_alarm_list==alarm_list) {
  364             Plast_alarm_list=alarm_list=NULL;
  365          }
  366          if (prev_alarm) {
  367             prev_alarm->next=temp_alarm->next;
  368          } else {
  369             /* Head of list */
  370             alarm_list=temp_alarm->next;
  371          }
  372          free(temp_alarm);
  373          return;
  374       } else {
  375          prev_alarm=temp_alarm;
  376          next_alarm=temp_alarm->next;
  377       }
  378    }
  379 }
  380 
  381 static void free_alarms_list(int mask)
  382 {
  383    struct jp_alarms *ta, *ta_next;
  384 
  385    if (mask&PREV_ALARM_MASK) {
  386       for (ta=alarm_list; ta; ta=ta_next) {
  387          ta_next=ta->next;
  388          free(ta);
  389       }
  390       Plast_alarm_list=alarm_list=NULL;
  391    }
  392 
  393    if (mask&NEXT_ALARM_MASK) {
  394       for (ta=next_alarm; ta; ta=ta_next) {
  395          ta_next=ta->next;
  396          free(ta);
  397       }
  398       next_alarm=NULL;
  399    }
  400 }
  401 
  402 static void alarms_write_file(void)
  403 {
  404    FILE *out;
  405    char line[256];
  406    int fail, n;
  407    time_t ltime;
  408    struct tm *now;
  409 
  410    jp_logf(JP_LOG_DEBUG, "alarms_write_file()\n");
  411 
  412    time(&ltime);
  413    now = localtime(&ltime);
  414    /* Alarm is triggered within ALARM_INTERVAL/2 seconds of the minute.
  415     * Potentially need to round timestamp up to the correct minute. */
  416    if ((59 - now->tm_sec) <= ALARM_INTERVAL/2) {
  417       now->tm_min++;
  418       mktime(now);
  419    }
  420    
  421    out=jp_open_home_file(EPN".alarms.tmp", "w");
  422    if (!out) {
  423       jp_logf(JP_LOG_WARN, _("Unable to open file: %s%s\n"), EPN, ".alarms.tmp");
  424       return;
  425    }
  426    fail=0;
  427    g_snprintf(line, sizeof(line), "%s",
  428            "# This file was generated by "EPN", changes will be lost\n");
  429    n = fwrite(line, strlen(line), 1, out);
  430    if (n<1) fail=1;
  431 
  432    g_snprintf(line, sizeof(line), "%s",
  433            "# This is the last time that "EPN" was ran\n");
  434    n = fwrite(line, strlen(line), 1, out);
  435    if (n<1) fail=1;
  436 
  437    sprintf(line, "UPTODATE %d %d %d %d %d\n",
  438            now->tm_year+1900,
  439            now->tm_mon+1,
  440            now->tm_mday,
  441            now->tm_hour,
  442            now->tm_min
  443           );
  444    n = fwrite(line, strlen(line), 1, out);
  445    if (n<1) fail=1;
  446 
  447    fclose(out);
  448 
  449    if (fail) {
  450       unlink_file(EPN".alarms.tmp");
  451    } else {
  452       rename_file(EPN".alarms.tmp", EPN".alarms");
  453    }
  454 }
  455 
  456 /*
  457  * This attempts to make the command safe.
  458  * I'm sure I'm missing things.
  459  */
  460 static void make_command_safe(char *command)
  461 {
  462    int i, len;
  463    char c;
  464 
  465    len = strlen(command);
  466    for (i=0; i<len; i++) {
  467       c=command[i];
  468       if (strchr("\r\n|&;()<>", c)) {
  469          command[i]=' ';
  470       }
  471    }
  472 }
  473 
  474 /*
  475  * Process an alarm occurrence
  476  *   Pop up alarm window.
  477  *   Do alarm setting (play sound, or whatever).
  478  *   if user postpones then put in postponed alarm list.
  479  */
  480 static int alarms_do_one(struct CalendarEvent *cale,
  481                          unsigned long unique_id,
  482                          time_t t_alarm,
  483                          AlarmType type)
  484 {
  485    struct tm *Pnow;
  486    struct tm begin;
  487    struct tm end;
  488    char time_str[255];
  489    char desc_str[255];
  490    char note_str[255];
  491    char pref_time[50];
  492    char time1_str[50];
  493    char time2_str[50];
  494    char date_str[50];
  495    char command[1024];
  496    char *reason;
  497    long wants_windows;
  498    long do_command;
  499    const char *pref_date;
  500    const char *pref_command;
  501    char c1, c2;
  502    int i, len;
  503 
  504    alarms_write_file();
  505 
  506    switch (type) {
  507     case ALARM_NONE:
  508       return EXIT_SUCCESS;
  509     case ALARM_NEW:
  510       reason=_("Appointment Reminder");
  511       break;
  512     case ALARM_MISSED:
  513       reason=_("Past Appointment");
  514       break;
  515     case ALARM_POSTPONED:
  516       reason=_("Postponed Appointment");
  517       break;
  518     default:
  519       reason=_("Appointment");
  520    }
  521    get_pref(PREF_SHORTDATE, NULL, &pref_date);
  522    get_pref_time_no_secs(pref_time);
  523 
  524    Pnow = localtime(&t_alarm);
  525 
  526    strftime(date_str, sizeof(date_str), pref_date, Pnow);
  527    tm_copy_with_dst_adj(&begin, &(cale->begin));
  528    strftime(time1_str, sizeof(time1_str), pref_time, &begin);
  529    tm_copy_with_dst_adj(&end, &(cale->end));
  530    strftime(time2_str, sizeof(time2_str), pref_time, &end);
  531    if (strcmp(time1_str,time2_str) == 0)
  532       g_snprintf(time_str, sizeof(time_str), "%s %s", date_str, time1_str);
  533    else
  534       g_snprintf(time_str, sizeof(time_str), "%s %s-%s", date_str, time1_str, time2_str);
  535 
  536    desc_str[0]='\0';
  537    note_str[0]='\0';
  538    if (cale->description) {
  539       g_strlcpy(desc_str, cale->description, sizeof(desc_str));
  540    }
  541    if (cale->note) {
  542       g_strlcpy(note_str, cale->note, sizeof(note_str));
  543    }
  544 
  545    get_pref(PREF_ALARM_COMMAND, NULL, &pref_command);
  546    get_pref(PREF_DO_ALARM_COMMAND, &do_command, NULL);
  547 #ifdef ALARMS_DEBUG
  548    printf("pref_command = [%s]\n", pref_command);
  549 #endif
  550    memset(command, 0, sizeof(command));
  551    if (do_command) {
  552       command[0]='\0';
  553       for (i=0; i<MAX_PREF_LEN-1; i++) {
  554          c1 = pref_command[i];
  555          len = strlen(command);
  556          if (c1=='%') {
  557             c2 = pref_command[i+1];
  558             /* expand '%t' */
  559             if (c2=='t') {
  560                i++;
  561                strncat(command, time1_str, sizeof(command)-2-len);
  562                continue;
  563             }
  564             /* expand '%d' */
  565             if (c2=='d') {
  566                i++;
  567                strncat(command, date_str, sizeof(command)-2-len);
  568                continue;
  569             }
  570 #ifdef ENABLE_ALARM_SHELL_DANGER
  571             /* expand '%D' */
  572             if (c2=='D') {
  573                i++;
  574                strncat(command, desc_str, sizeof(command)-2-len);
  575                continue;
  576             }
  577             /* expand '%N' */
  578             if (c2=='N') {
  579                i++;
  580                strncat(command, note_str, sizeof(command)-2-len);
  581                continue;
  582             }
  583 #endif
  584          }
  585          if (len<sizeof(command)-4) {
  586             command[len++]=c1;
  587             command[len]='\0';
  588          }
  589          if (c1=='\0') {
  590             break;
  591          }
  592       }
  593       command[sizeof(command)-2]='\0';
  594 
  595       make_command_safe(command);
  596       jp_logf(JP_LOG_STDOUT|JP_LOG_FILE, _("executing command = [%s]\n"), command);
  597       if (system(command) == -1) {
  598          jp_logf(JP_LOG_WARN, "system call failed %s %d\n", __FILE__, __LINE__);
  599       }
  600    }
  601 
  602    get_pref(PREF_OPEN_ALARM_WINDOWS, &wants_windows, NULL);
  603 
  604    if (wants_windows) {
  605       return dialog_alarm(_("J-Pilot Alarm"), reason,
  606                           time_str, desc_str, note_str,
  607                           unique_id,
  608                           t_alarm);
  609    }
  610    return EXIT_SUCCESS;
  611 }
  612 
  613 /*
  614  * See if next_alarm is due in less than ALARM_INTERVAL/2 secs.
  615  * If it is, then do_alarm and find_next_alarm.
  616  */
  617 static gint cb_timer_alarms(gpointer data)
  618 {
  619    struct jp_alarms *temp_alarm, *ta_next;
  620    CalendarEventList *alm_list;
  621    CalendarEventList *temp_al;
  622    static int first=1;
  623    time_t t, diff;
  624    time_t t_alarm_time;
  625    struct tm *Ptm;
  626    struct tm copy_tm;
  627 
  628    alm_list=NULL;
  629 
  630    if (first) {
  631       alarms_write_file();
  632       first=0;
  633    }
  634 
  635    time(&t);
  636 
  637    for (temp_alarm=alarm_list; temp_alarm; temp_alarm=ta_next) {
  638       ta_next=temp_alarm->next;
  639       diff = temp_alarm->event_time - t - temp_alarm->alarm_advance;
  640       if (temp_alarm->type!=ALARM_MISSED) {
  641          if (diff >= ALARM_INTERVAL/2) {
  642             continue;
  643          }
  644       }
  645       if (alm_list==NULL) {
  646          get_days_calendar_events2(&alm_list, NULL, 0, 0, 1, CATEGORY_ALL, NULL);
  647       }
  648 #ifdef ALARMS_DEBUG
  649       printf("unique_id=%d\n", temp_alarm->unique_id);
  650       printf("type=%s\n", print_type(temp_alarm->type));
  651       printf("event_time=%s\n", print_date(temp_alarm->event_time));
  652       printf("alarm_advance=%ld\n", temp_alarm->alarm_advance);
  653 #endif
  654       for (temp_al = alm_list; temp_al; temp_al=temp_al->next) {
  655          if (temp_al->mcale.unique_id == temp_alarm->unique_id) {
  656 #ifdef ALARMS_DEBUG
  657             printf("%s\n", temp_al->mcale.cale.description);
  658 #endif
  659             alarms_do_one(&(temp_al->mcale.cale),
  660                           temp_alarm->unique_id,
  661                           temp_alarm->event_time,
  662                           ALARM_MISSED);
  663             break;
  664          }
  665       }
  666       /* CAUTION, this modifies the list we are parsing and
  667        * removes the current node */
  668       if (temp_al)
  669          alarms_remove_from_to_list(temp_al->mcale.unique_id);
  670    }
  671 
  672    if (next_alarm) {
  673       diff = next_alarm->event_time - t - next_alarm->alarm_advance;
  674       if (diff <= ALARM_INTERVAL/2) {
  675          if (alm_list==NULL) {
  676             get_days_calendar_events2(&alm_list, NULL, 0, 0, 1, CATEGORY_ALL, NULL);
  677          }
  678          for (temp_alarm=next_alarm; temp_alarm; temp_alarm=ta_next) {
  679             for (temp_al = alm_list; temp_al; temp_al=temp_al->next) {
  680                if (temp_al->mcale.unique_id == temp_alarm->unique_id) {
  681 #ifdef ALARMS_DEBUG
  682                   printf("** next unique_id=%d\n", temp_alarm->unique_id);
  683                   printf("** next type=%s\n", print_type(temp_alarm->type));
  684                   printf("** next event_time=%s\n", print_date(temp_alarm->event_time));
  685                   printf("** next alarm_advance=%ld\n", temp_alarm->alarm_advance);
  686                   printf("** next %s\n", temp_al->mcale.cale.description);
  687 #endif
  688                   alarms_do_one(&(temp_al->mcale.cale),
  689                                 temp_alarm->unique_id,
  690                                 temp_alarm->event_time,
  691                                 ALARM_NEW);
  692                   break;
  693                }
  694             }
  695             /* This may not be exactly right */
  696             t_alarm_time = temp_alarm->event_time + 1;
  697 #ifdef ALARMS_DEBUG
  698             printf("** t_alarm_time-->%s\n", print_date(t_alarm_time));
  699 #endif
  700             ta_next=temp_alarm->next;
  701             free(temp_alarm);
  702             next_alarm = ta_next;
  703          }
  704          Ptm = localtime(&t_alarm_time);
  705          memcpy(&copy_tm, Ptm, sizeof(struct tm));
  706          alarms_find_next(&copy_tm, &copy_tm, TRUE);
  707       }
  708    }
  709    if (alm_list) {
  710       free_CalendarEventList(&alm_list);
  711    }
  712 
  713    return TRUE;
  714 }
  715 
  716 
  717 /*
  718  * Find the next appointment alarm
  719  *  if soonest_only then return the next alarm, 
  720  *  else return all alarms that occur between the two dates.
  721  */
  722 int alarms_find_next(struct tm *date1_in, struct tm *date2_in, int soonest_only)
  723 {
  724    CalendarEventList *alm_list;
  725    CalendarEventList *temp_al;
  726    struct jp_alarms *ta;
  727 
  728    time_t adv;
  729    time_t ltime;
  730    time_t t1, t2;
  731    time_t t_alarm;
  732    time_t t_end;
  733    time_t t_prev;
  734    time_t t_future;
  735    struct tm *tm_temp;
  736    struct tm date1, date2;
  737    struct tm tm_prev, tm_next;
  738    int prev_found, next_found;
  739    int add_a_next;
  740 
  741    jp_logf(JP_LOG_DEBUG, "alarms_find_next()\n");
  742 
  743    if (glob_skip_all_alarms) return EXIT_SUCCESS;
  744 
  745    if (!date1_in) {
  746       time(&ltime);
  747       tm_temp = localtime(&ltime);
  748    } else {
  749       tm_temp=date1_in;
  750    }
  751    memset(&date1, 0, sizeof(date1));
  752    date1.tm_year=tm_temp->tm_year;
  753    date1.tm_mon=tm_temp->tm_mon;
  754    date1.tm_mday=tm_temp->tm_mday;
  755    date1.tm_hour=tm_temp->tm_hour;
  756    date1.tm_min=tm_temp->tm_min;
  757    date1.tm_sec=tm_temp->tm_sec;
  758    date1.tm_isdst=tm_temp->tm_isdst;
  759 
  760    if (!date2_in) {
  761       time(&ltime);
  762       tm_temp = localtime(&ltime);
  763    } else {
  764       tm_temp=date2_in;
  765    }
  766    memset(&date2, 0, sizeof(date2));
  767    date2.tm_year=tm_temp->tm_year;
  768    date2.tm_mon=tm_temp->tm_mon;
  769    date2.tm_mday=tm_temp->tm_mday;
  770    date2.tm_hour=tm_temp->tm_hour;
  771    date2.tm_min=tm_temp->tm_min;
  772    date2.tm_sec=tm_temp->tm_sec;
  773    date2.tm_isdst=tm_temp->tm_isdst;
  774 
  775    t1=mktime_dst_adj(&date1);
  776    t2=mktime_dst_adj(&date2);
  777 
  778 #ifdef ALARMS_DEBUG
  779    char str[100];
  780    struct tm *Pnow;
  781 
  782    strftime(str, sizeof(str), "%B %d, %Y %H:%M", &date1);
  783    printf("date1=%s\n", str);
  784    strftime(str, sizeof(str), "%B %d, %Y %H:%M", &date2);
  785    printf("date2=%s\n", str);
  786    Pnow = localtime(&t1);
  787    strftime(str, sizeof(str), "%B %d, %Y %H:%M", Pnow);
  788    printf("[Now]=%s\n", str);
  789 #endif
  790 
  791    if (!soonest_only) {
  792       free_alarms_list(PREV_ALARM_MASK | NEXT_ALARM_MASK);
  793    } else {
  794       free_alarms_list(NEXT_ALARM_MASK);
  795    }
  796 
  797    alm_list=NULL;
  798    get_days_calendar_events2(&alm_list, NULL, 0, 0, 1, CATEGORY_ALL, NULL);
  799 
  800    for (temp_al=alm_list; temp_al; temp_al=temp_al->next) {
  801       /* No alarm, skip */
  802       if (!temp_al->mcale.cale.alarm) {
  803          continue;
  804       }
  805 #ifdef ALARMS_DEBUG
  806       printf("\n[%s]\n", temp_al->mcale.cale.description);
  807 #endif
  808       /* Check for ordinary non-repeating appt starting before date1 */
  809       if (temp_al->mcale.cale.repeatType == calendarRepeatNone) {
  810          t_alarm = mktime_dst_adj(&(temp_al->mcale.cale.begin));
  811          if (t_alarm < t1) {
  812 #ifdef ALARMS_DEBUG
  813             printf("afn: non repeat before t1, t_alarm<t1, %ld<%ld\n",t_alarm,t1);
  814 #endif
  815             continue;
  816          }
  817       }
  818 
  819       /* Check that we are not past any appointment end date */
  820       if (!(temp_al->mcale.cale.repeatForever)) {
  821          t_end = mktime_dst_adj(&(temp_al->mcale.cale.repeatEnd));
  822          /* We need to add 24 hours to the end date to make it inclusive */
  823          t_end += DAY_IN_SECS;
  824          if (t_end < t2) {
  825 #ifdef ALARMS_DEBUG
  826             printf("afn: past end date\n");
  827 #endif
  828             continue;
  829          }
  830       }
  831 
  832       /* Calculate the alarm advance in seconds */
  833       adv = 0;
  834       switch (temp_al->mcale.cale.advanceUnits) {
  835        case advMinutes:
  836          adv = temp_al->mcale.cale.advance*MIN_IN_SECS;
  837          break;
  838        case advHours:
  839          adv = temp_al->mcale.cale.advance*HR_IN_SECS;
  840          break;
  841        case advDays:
  842          adv = temp_al->mcale.cale.advance*DAY_IN_SECS;
  843          break;
  844       }
  845 
  846 #ifdef ALARMS_DEBUG
  847       printf("alarm advance %d ", temp_al->mcale.cale.advance);
  848       switch (temp_al->mcale.cale.advanceUnits) {
  849        case advMinutes:
  850          printf("minutes\n");
  851          break;
  852        case advHours:
  853          printf("hours\n");
  854          break;
  855        case advDays:
  856          printf("days\n");
  857          break;
  858       }
  859       printf("adv=%ld\n", adv);
  860 #endif
  861 
  862       prev_found=next_found=0;
  863 
  864       find_prev_next(&(temp_al->mcale.cale),
  865                      adv,
  866                      &date1,
  867                      &date2,
  868                      &tm_prev,
  869                      &tm_next,
  870                      &prev_found,
  871                      &next_found);
  872       t_prev=mktime_dst_adj(&tm_prev);
  873       t_future=mktime_dst_adj(&tm_next);
  874 
  875       /* Skip the alarms if they are before date1 or after date2 */
  876       if (prev_found) {
  877          if (t_prev - adv < t1) {
  878 #ifdef ALARMS_DEBUG
  879             printf("failed prev is before t1\n");
  880 #endif
  881             prev_found=0;
  882          }
  883          if (t_prev - adv > t2) {
  884 #ifdef ALARMS_DEBUG
  885             printf("failed prev is after t2\n");
  886 #endif
  887             continue;
  888          }
  889       }
  890       if (next_found) {
  891          /* Check that we are not past any appointment end date */
  892          if (!(temp_al->mcale.cale.repeatForever)) {
  893             t_end = mktime_dst_adj(&(temp_al->mcale.cale.repeatEnd));
  894             /* We need to add 24 hours to the end date to make it inclusive */
  895             t_end += DAY_IN_SECS;
  896             if (t_future > t_end) {
  897 #ifdef ALARMS_DEBUG
  898                printf("failed future is after t_end\n");
  899 #endif
  900                next_found=0;
  901             }
  902          }
  903       }
  904 #ifdef ALARMS_DEBUG
  905       printf("t1=       %s\n", print_date(t1));
  906       printf("t2=       %s\n", print_date(t2));
  907       printf("t_prev=   %s\n", prev_found ? print_date(t_prev):"None");
  908       printf("t_future= %s\n", next_found ? print_date(t_future):"None");
  909       printf("alarm me= %s\n", next_found ? print_date(t_future-adv):"None");
  910       printf("desc=[%s]\n", temp_al->mcale.cale.description);
  911 #endif
  912 
  913       if (!soonest_only) {
  914          if (prev_found) {
  915             alarms_add_to_list(temp_al->mcale.unique_id, ALARM_MISSED, t_prev, adv);
  916          }
  917       }
  918       if (next_found) {
  919          add_a_next=0;
  920          if (next_alarm==NULL) {
  921             add_a_next=1;
  922          } else if
  923            (t_future - adv <= (next_alarm->event_time - next_alarm->alarm_advance)) {
  924               add_a_next=1;
  925               if (t_future - adv < (next_alarm->event_time - next_alarm->alarm_advance)) {
  926 #ifdef ALARMS_DEBUG
  927                  printf("next alarm=%s\n", print_date(next_alarm->event_time - next_alarm->alarm_advance));
  928                  printf("freeing next alarms\n");
  929 #endif
  930                  free_alarms_list(NEXT_ALARM_MASK);
  931               }
  932            }
  933 
  934          if (add_a_next) {
  935 #ifdef ALARMS_DEBUG
  936             printf("found a new next\n");
  937 #endif
  938             ta = malloc(sizeof(struct jp_alarms));
  939             if (ta) {
  940                ta->next = next_alarm;
  941                next_alarm = ta;
  942                next_alarm->unique_id = temp_al->mcale.unique_id;
  943                next_alarm->type = ALARM_NEW;
  944                next_alarm->event_time = t_future;
  945                next_alarm->alarm_advance = adv;
  946             }
  947          }
  948       }
  949    }
  950    free_CalendarEventList(&alm_list);
  951 
  952    return EXIT_SUCCESS;
  953 }
  954 
  955 /*
  956  * At startup check when rc file was written and find all past-due alarms.
  957  * Add them to the postponed alarm list with 0 minute reminder.
  958  * Find next alarm and put it in list
  959  */
  960 int alarms_init(unsigned char skip_past_alarms,
  961                 unsigned char skip_all_alarms)
  962 {
  963    FILE *in;
  964    time_t ltime;
  965    struct tm now, *Pnow;
  966    struct tm tm1;
  967    char line[256];
  968    int found_uptodate;
  969    int year, mon, day, hour, min, n;
  970 
  971    jp_logf(JP_LOG_DEBUG, "alarms_init()\n");
  972 
  973    alarm_list=NULL;
  974    Plast_alarm_list=NULL;
  975    next_alarm=NULL;
  976 
  977    total_alarm_windows = 0;
  978    glob_skip_all_alarms = skip_all_alarms;
  979 
  980    if (skip_past_alarms) {
  981       alarms_write_file();
  982    }
  983    if (skip_all_alarms) {
  984       alarms_write_file();
  985       return EXIT_SUCCESS;
  986    }
  987 
  988    found_uptodate=0;
  989    in=jp_open_home_file(EPN".alarms", "r");
  990    if (!in) {
  991       jp_logf(JP_LOG_WARN, _("Unable to open file: %s%s\n"), EPN, ".alarms");
  992       return EXIT_FAILURE;
  993    }
  994 
  995    while (!feof(in)) {
  996       line[0]='\0';
  997       if (fgets(line, sizeof(line)-1, in) == NULL) {
  998          // jp_logf(JP_LOG_WARN, "fgets failed %s %d\n", __FILE__, __LINE__);
  999          break;
 1000       }
 1001       line[sizeof(line)-1] = '\0';
 1002       if (line[0]=='#') continue;
 1003       if (!strncmp(line, "UPTODATE ", 9)) {
 1004          n = sscanf(line+9, "%d %d %d %d %d\n", &year, &mon, &day, &hour, &min);
 1005          if (n==5) {
 1006             found_uptodate=1;
 1007          }
 1008          /* Avoid corner case of retriggering alarms that are set for 
 1009           * exactly the same time as the UPTODATE timestamp */
 1010          min = min + 1;
 1011          jp_logf(JP_LOG_DEBUG, "UPTODATE %d %d %d %d %d\n", year, mon, day, hour, min);
 1012       }
 1013    }
 1014 
 1015    time(&ltime);
 1016    Pnow = localtime(&ltime);
 1017    memset(&now, 0, sizeof(now));
 1018    now.tm_year=Pnow->tm_year;
 1019    now.tm_mon=Pnow->tm_mon;
 1020    now.tm_mday=Pnow->tm_mday;
 1021    now.tm_hour=Pnow->tm_hour;
 1022    now.tm_min=Pnow->tm_min;
 1023    now.tm_isdst=-1;
 1024    mktime(&now);
 1025 
 1026    /* Corner case where current time == UPTODATE time.
 1027     * Must adjust current time forward by 1 minute so that upper 
 1028     * bound of search range (current time) is not smaller than
 1029     * lower bound (UPTODATE time) */
 1030    if (found_uptodate &&
 1031       (now.tm_min  == min-1) &&
 1032       (now.tm_hour == hour)  &&
 1033       (now.tm_mon  == mon-1) &&
 1034       (now.tm_year == year-1900)) {
 1035       now.tm_min += 1;
 1036       mktime(&now);
 1037    }
 1038 
 1039    /* No UPTODATE, use current time for search lower bound */
 1040    if (!found_uptodate) {
 1041       alarms_write_file();
 1042       year = now.tm_year+1900;
 1043       mon = now.tm_mon+1;
 1044       day = now.tm_mday;
 1045       hour = now.tm_hour;
 1046       min = now.tm_min;
 1047    }
 1048 
 1049    memset(&tm1, 0, sizeof(tm1));
 1050    tm1.tm_year=year-1900;
 1051    tm1.tm_mon=mon-1;
 1052    tm1.tm_mday=day;
 1053    tm1.tm_hour=hour;
 1054    tm1.tm_min=min;
 1055    tm1.tm_isdst=-1;
 1056 
 1057    mktime(&tm1);
 1058 
 1059    alarms_find_next(&tm1, &now, FALSE);
 1060 
 1061    /* Pop up reminder windows for expired alarms immediately
 1062     * rather than waiting ALARM_INTERVAL seconds and then doing it */
 1063    cb_timer_alarms(NULL);
 1064 
 1065    g_timeout_add(ALARM_INTERVAL*CLOCK_TICK, cb_timer_alarms, NULL);
 1066 
 1067    return EXIT_SUCCESS;
 1068 }