aboutsummaryrefslogblamecommitdiffstats
path: root/src/io.c
blob: 8709341c9bc7d3b49ac2cff69665054428482a38 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                  
  
                                                                        
                       
  


                                                                     
  



















                                                                        
  
                                                        
                                           


   

                   
                     
                      
                   


                   
                  
 
                     
 


                                        
              


                     

                 












                              
              





               





                



                        

             
                         

                  


                                  





































                                                                      

                                                                  

                                                

                              



                          


                                        
                                                                      
                      
                         

                     

                     
    
                            



                     

                             


                                 


                                                               
                             
                            



                           
                                                                              


                                             
                                                                              


                                              

                                                                        





                                                
                                                      




                                                 
                             
                           



                 

 
                                                 
             
                                         
 
               
             




                                                                      
                                                            

                
                                             
                                       

                                                                
      

                                                                          





                                                             



                                 

                                        



                                                
     
                         


                  
 

                                                                           
                                

           
                                                                      


                                                                        









                                         
 

                                                

                                                                       
         
                                                                    















                                                                   
                                                 





                          
                              
           
                                 
 
                                           


                                                         

                                             

 
                    
           
                                 
 


                                                                           

 


                                 







                                                                                
 
 
                    
           
                                 
 
                                            

 




                                 
                              
           
                                       
 
                      

                         
                                 
     

                                                           


                                                         
                                                                      
 
                               
         
                                                                      

                                                           
          
                                     
 
                                  
         

                                            
             






                                                                  
             
         
 
                                                        
                                             
     

 







                                                               
                                                            











                                                                  

                                                                             




                                       
                  

                         




                                                                               
 
                                 
     

                                                      
         
                                 

                             
                                                               
                                                                    
                                                   

                              
                                                            
                                                                       

                                                                       

                               


                                                                      

                              

                                                                       

                    
                                                     






                                                            
                                                           
                                                                          
                                                                               



         
                    
           
                                 
 
                  

                         
                               
     

                                                          

                                                        
                                                       
                                             
     

 


                                 
                  
 
                                                             




                                                     
                               
 
 
                                    
           
                                        
 
                      


                             

                                      
     


                                                                      

                                                            
                                                                 
                                                         
                                                                        
 
                                
         

                                                                       

                                                           
          
                                     
 
                                   
         

                                            
             






                                                                  
             
         
 

                                                         
                                    
                                             
     
                                   

 


                                        
                  
                                                             
 


                                                 
                        
                                                                               
 
                                      
     


                                                        
         


                                                                      

                             
                                                                  
                                                                               
                                                                        

                              
                                                               
                                                                       
                                                                  
                                                                      
                                                             

                               
                                                               
                                                                      
                                                                        

                              
                                                                  
                                                                      
                                                             

                    
                                                     






                                                            
                                                                 
                                                                            

                                                                    



         
                          
           
                                  
 
                  

                             

                                
     

                                                                     

                                                            




                                                             

                                                        
                                    
                                             
     
                             

 


                                  
                  
 
                                                                               






                                                                 
                               

 
                        
           
                               
 
                  
 
                              
     

                                                
                 
 
                                              

                                                         
                                            
     

 


                               
                  
 
                                            
                              
     

                                                
                 
 
                                           
                                                               
     
                               

 
















                                                         
 

                                                                     
 






                                                             
  
                                                                   


                                                                       
                                                                           

    
                                    
 




                              
                      
     
                     




                                                                      
                                                                     
                                                                     
                                                                     
                                                                         


      




                             


                                                                
                                                                
                                                                

                                                                    
                                                                 

                        
                                                                    


          

                                                          











                                                                        
                                               







                                                     
                                                   









                                                                         
                                               


                        
                                               
         
     

 

                                                          
 
        
 
                               

                                       
              


                           

 






                                   
 

                                         
                       

                               
                       
                
                   



                                                                 

                                  
                                
 









                                                                              
                          
           
 
                                            




                                         
 



                                                               
 



                                                                      
 



                                                                  
 



                                                                        
 



                                                                                 
 




                                                                            



                                                                          
 




                                                                             
 


                                                                       
 


                                                                   




                                                                        
 






                                                         
 



                                                                                 
 



                                                                                 
 




                                                                      
 



                                                          
 



                                                              
 




                                                                  
 

                                       










                                                                           
 




                                


                                              




                                     
                  

           
                                            




                           





                                                 
                           
                               
 




                                               








                                
                  
           
 
                                            

             
                              
     


                                                                             
          
                                                              










                                
 
                                            
             
 







                                
                                                          

                                                          

                                                                   
               
 
                                      
 
               

                                                            
                 

                                                                   
 
               
                                                        
                           
                                
 
               
                                                        
                       
                                
 
               
                                                        
                       
                                
 

                                                        
                       
                                
 
                                               

                                                        

                                        
                                
     

                                        

 

                                                              

                                                                        


                  
 


                                                
              




                                 







                                     
                        


                                                   
              
                                  

                                                         
                          

                                             

                                                                      
                                                    
         
 





                                                               
                           
                        
                     
          
         
                                                     
         
                                  


                                            





                                                                    
                        


                                           
          
         

                                                               
         




                                              




















                                                                     
                                                   










                                                               
                                               




                                   

                                                                   

             
          
                                    



                                                    





                                                     
          



                                      





                                                               























                                                                        
                        










                                                           
          
         

                                                               
         
     
                                       


                        
    
                   
 


                                                    
                







                                                      
                                




                           


                
                        


                                                       
          



                                      


                                               




                                                     
          
                       
                                       
                                               


                                   
                                                


                                  
                                       
                       

 
           


















                                                                         
  









                                                                              


                                                                      

                                                                     

    
                          
 
                                       

                   
                      

                               

               
 


                                                                 
 


                                                                         
                              
     

                                                   



                                                         
                                                             

                              




                                                
                   
 










                                                                        
                                            


                                     
                                          
                 
 
                                                   




                                                                  
                                 
                                       
                                                                   






                                                                  





                                              
                                            

                                                       
                     
 
                                                   



                                           

                                                                          

                                                        
                  
                 
                           
 

                                                               



                                                    
                                                               




                                                                              
                 
                                       

              




                           

         

                                     



                                                                           
 





                                                                      

                                     
                                                                          


            

                                      
 



                             

                                                                             
                                                

                                       

      



                     
 
 

                          
 
           
 


             
                                       


                    
 





                                        
            
                            
     
               
 

                     
                                           

                                                                             
                                                 

                                       
                                    
     
 
 

                                                   





                                             
                            


                            

                          
 
                            
 
                             
            

                                      



                                           





                                     
                 


                             
    
                                                           
 







                                                                    
                                



                                     
                                
     

 

                           
                                                         
 
               


                                                           
                                                         


                                    
                  
     
                    

                      
                   
                                        

            
                                    


                      
                     

           
                                  
 

                                                                   

                                        
 

                                                                    

                                         
 

                                                                 
                                
 
                                  

                       
                                      
 
                                                         

                                   
                                
     
 





















                                                         
                   
 
 


















                                                                                

          
                                         












                                                                                
                                                                     
          
                                                                     

 
           


                                                      
                  
                                            


           
                                                                              
                               

                        
 


                                                                      
                                        


                             
     





                                                               
                                                                      
                                        
                     




                                           
                  
                                            



                                                                
                                                                




                           
          

                                                                            
                                         
                     




                                                 
                  
                                            



                                                                             
               












                                                                                










                                  


























                                      

                            
 
 

                                                                     
 
            
 





                                           
 



                                                                
 

                                
 






                                               
                             







                       
     

           

 
            
                                                                     
 
                                  
                                                                    
                
 

                                               




                                                         
                                               
     

                                                   
     
                 
















                                                                              
                                                               
                   

                     
             
 


                                                                


                      
                                       
     
                                     


                            




                                            










































































                                                                          
 

                                                                        







                                                                        
         
                                                                        
             






                                                    








                               



































                                                                            





                                                                       
  




























                                                                
                   

                                                                
 





                                                          
                    
                  
          
 
             

                                           
                           
 
          
                                             
                                       
                                              
         

                                                          
                         
                         
                      












                                                                    

                                                                   
                             
                             
                          











                                                                         
                                                   
         

                         
                                     
                                                             


          

                         
 
                                                                
             









                                                     

              
                           
         

                                                      
         









                                                       
         


                        
         


      
                                                                             
                     
     
             


           
                                           
 
                
     
                                                           
                     

                                






                                                               

                                                                            
                                     
 


              
                   







                                           
                                         

                                                
                                   


                                          
                               


      

                                                            

                     

 
                                                                               
             

                                                                         
 
                                                     
            
 
                                       
     



                                                                    
                                                                          


                                                                         
          
                                       
                          
         
                                             
                                                                 
                                         


                                              
         

                                     
                                         
                                              
                             



                      

                                             


                             


      
                                                                       




                     

                                                                   
                              


                    
                                       

          
                                       





                     
           
                                                                               

                                                              
 
                               
                                                             
                                                          

                                                          
                                                           


                                                               
                                                             
                                                              
                            
                             
          
                      
                      
                              
                                  
                            

                 
 


                                 
                                                  
     

                                       
                              
 
                     








                                                                       
                          
             


                                                                         
 




                                        
                                                           
                                                                     
                                   




                                          
                                                               

                                                                   
                                       



                                                          
                                          
                                                                     
                                                                     
                                                                     
                                        


                                 








                                                                            
                     
                                                                            
                                                                         
                                                       


                                 

                                        
                                                           


                                                                 
                                                                           
                                                                         


                                
                                                       
                                                                
                               
                        
                 


              


                                                               






                                                                 
                                                  
                                                                      
                                
                 

                                                                       
                               



                                                                  
                                                  
                                                                    
                              
                 

                                                                     
                               





                                                                        

                                                          
                               



                                                                  
                                                                           


                                                                    
                                                                            
             

                                                                      
                                                    







                                                                  

                                                                        


             


                                                     

        






                           
                           
                 


           
                                                                             
                                                          
 




                                                               
                       
                               
                         
          

                               

                 
 

                               
                                                  
     

                                       
                              
                     










                                                                       
                         
             
                                                                       



                           


                                                               





                     
 








                                                              


                                                               




                                                                      
                                                   






                                                                  

                                                                      


             


                                                     

        




                          
                 

 
             
                                         









                                                                      
                                    
                              




                                                             



                                 

                                        



                                                
     
                         



                





                                                                                
    
                                                                            
 

                                                             
                                                                   
                     


                                                                           
                                   
                      
                      
                     



                                                    
                                                                            
                  
     
                    



                                                                   
            
                   


                                        
                                                 

                      



                     
                               

                                                                     
                              
                                                                         

                               
                       
                  

                          
                                          


                                        
 
                                                           


                        

                                                     
                                                                          
                                                                      


                                                        
                                                                        
                                                     


                      
                                      
 


                                         
                                                         


                                      


                                                                           
                               
                                
     
                                 


                                        


                                                                    
     
 


                                                                           
                                     


                                                                             
 



                                                  
 
                

                  
                         
                
                      
 
                                                                   
                                              
                                
                                                                             
                                             
                            
                                                                           
                                                                             

                                   
                      









                                                                               
                                                       

                     
                                                        


    
                                                            



                           
                                                        






                                       
 

                                                                  
         




                                 
         

                                    
             
                                                      
             
         
                                       
                               
     


    
                                 






                                                                            
 






                                            
                      

            
                              

                                          
 








                                                     
                                         







                                                                    
                 



                                      
 



                                                                          
  




                                                                              




                  














                                                                      
 
                   





                                                                               
                                                              
                          


      


                                                          
     
 
 
  

                                                                              


                        
 

            
 







                                             
 
           
 














                                                                    
                                       








                                   










                                       
                                                                 










                      
            
 
/*
 * Calcurse - text-based organizer
 *
 * Copyright (c) 2004-2011 calcurse Development Team <misc@calcurse.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the
 *        following disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the
 *        following disclaimer in the documentation and/or other
 *        materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Send your feedback or comments to : misc@calcurse.org
 * Calcurse home page : http://calcurse.org
 *
 */

#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>
#include <time.h>
#include <math.h>
#include <unistd.h>
#include <errno.h>

#include "calcurse.h"

#define ICALDATEFMT      "%Y%m%d"
#define ICALDATETIMEFMT  "%Y%m%dT%H%M%S"

typedef enum {
  PROGRESS_BAR_SAVE,
  PROGRESS_BAR_LOAD,
  PROGRESS_BAR_EXPORT
} progress_bar_t;

enum {
  PROGRESS_BAR_CONF,
  PROGRESS_BAR_TODO,
  PROGRESS_BAR_APTS,
  PROGRESS_BAR_KEYS
};

enum {
  PROGRESS_BAR_EXPORT_EVENTS,
  PROGRESS_BAR_EXPORT_APOINTS,
  PROGRESS_BAR_EXPORT_TODO
};

typedef enum {
  ICAL_VEVENT,
  ICAL_VTODO,
  ICAL_TYPES
} ical_types_e;

typedef enum {
  UNDEFINED,
  APPOINTMENT,
  EVENT
} ical_vevent_e;

typedef struct {
  enum recur_type type;
  int             freq;
  long            until;
  unsigned        count;
} ical_rpt_t;

struct ht_keybindings_s {
  char     *label;
  enum key  key;
  HTABLE_ENTRY (ht_keybindings_s);
};

/* Type definition for callbacks to multiple-mode export functions. */
typedef void (*cb_export_t)(FILE *);
typedef void (*cb_dump_t)(FILE *, long, long, char *);

/* Static functions used to add export functionalities. */
static void ical_export_header (FILE *);
static void ical_export_recur_events (FILE *);
static void ical_export_events (FILE *);
static void ical_export_recur_apoints (FILE *);
static void ical_export_apoints (FILE *);
static void ical_export_todo (FILE *);
static void ical_export_footer (FILE *);

static void pcal_export_header (FILE *);
static void pcal_export_recur_events (FILE *);
static void pcal_export_events (FILE *);
static void pcal_export_recur_apoints (FILE *);
static void pcal_export_apoints (FILE *);
static void pcal_export_todo (FILE *);
static void pcal_export_footer (FILE *);

cb_export_t cb_export_header[IO_EXPORT_NBTYPES] =
  {ical_export_header, pcal_export_header};
cb_export_t cb_export_recur_events[IO_EXPORT_NBTYPES] =
  {ical_export_recur_events, pcal_export_recur_events};
cb_export_t cb_export_events[IO_EXPORT_NBTYPES] =
  {ical_export_events, pcal_export_events};
cb_export_t cb_export_recur_apoints[IO_EXPORT_NBTYPES] =
  {ical_export_recur_apoints, pcal_export_recur_apoints};
cb_export_t cb_export_apoints[IO_EXPORT_NBTYPES] =
  {ical_export_apoints, pcal_export_apoints};
cb_export_t cb_export_todo[IO_EXPORT_NBTYPES] =
  {ical_export_todo, pcal_export_todo};
cb_export_t cb_export_footer[IO_EXPORT_NBTYPES] =
  {ical_export_footer, pcal_export_footer};

static char *ical_recur_type[RECUR_TYPES] =
  { "", "DAILY", "WEEKLY", "MONTHLY", "YEARLY" };

/* Draw a progress bar while saving, loading or exporting data. */
static void
progress_bar (progress_bar_t type, int progress)
{
#define SLEEPTIME	125000
#define NBFILES		4
#define NBEXPORTED      3
#define LABELENGTH      15
  int i, step, steps;
  char *mesg_sav = _("Saving...");
  char *mesg_load = _("Loading...");
  char *mesg_export = _("Exporting...");
  char *error_msg = _("Internal error while displaying progress bar");
  char *barchar = "|";
  char *file[NBFILES] = {
    "[    conf    ]",
    "[    todo    ]",
    "[    apts    ]",
    "[    keys    ]"
  };
  char *data[NBEXPORTED] = {
    "[   events   ]",
    "[appointments]",
    "[    todo    ]"
  };
  int ipos =  LABELENGTH + 2;
  int epos[NBFILES];

  /* progress bar length init. */
  ipos = LABELENGTH + 2;
  steps = (type == PROGRESS_BAR_EXPORT) ? NBEXPORTED : NBFILES;
  step = floor (col / (steps + 1));
  for (i = 0; i < steps - 1; i++)
    epos[i] = (i + 2) * step;
  epos[steps - 1] = col - 2;

  switch (type)
    {
    case PROGRESS_BAR_SAVE:
      EXIT_IF (progress < 0 || progress > PROGRESS_BAR_KEYS, "%s", error_msg);
      status_mesg (mesg_sav, file[progress]);
      break;
    case PROGRESS_BAR_LOAD:
      EXIT_IF (progress < 0 || progress > PROGRESS_BAR_KEYS, "%s", error_msg);
      status_mesg (mesg_load, file[progress]);
      break;
    case PROGRESS_BAR_EXPORT:
      EXIT_IF (progress < 0
               || progress > PROGRESS_BAR_EXPORT_TODO, "%s", error_msg);
      status_mesg (mesg_export, data[progress]);
      break;
    }

  /* Draw the progress bar. */
  mvwprintw (win[STA].p, 1, ipos, barchar);
  mvwprintw (win[STA].p, 1, epos[steps - 1], barchar);
  custom_apply_attr (win[STA].p, ATTR_HIGHEST);
  for (i = ipos + 1; i < epos[progress]; i++)
    mvwaddch (win[STA].p, 1, i, ' ' | A_REVERSE);
  custom_remove_attr (win[STA].p, ATTR_HIGHEST);
  wmove (win[STA].p, 0, 0);
  wins_wrefresh (win[STA].p);
  (void)usleep (SLEEPTIME);
#undef SLEEPTIME
#undef NBFILES
#undef NBEXPORTED
#undef LABELENGTH
}

/* Ask user for a file name to export data to. */
static FILE *
get_export_stream (enum export_type type)
{
  FILE *stream;
  int cancel;
  char *home, *stream_name;
  char *question = _("Choose the file used to export calcurse data:");
  char *wrong_name =
    _("The file cannot be accessed, please enter another file name.");
  char *press_enter = _("Press [ENTER] to continue.");
  const char *file_ext[IO_EXPORT_NBTYPES] = {"ical", "txt"};

  stream = NULL;
  stream_name = (char *) mem_malloc (BUFSIZ);
  if ((home = getenv ("HOME")) != NULL)
    (void)snprintf (stream_name, BUFSIZ, "%s/calcurse.%s", home,
                    file_ext[type]);
  else
    (void)snprintf (stream_name, BUFSIZ, "%s/calcurse.%s", get_tempdir (),
                    file_ext[type]);

  while (stream == NULL)
    {
      status_mesg (question, "");
      cancel = updatestring (win[STA].p, &stream_name, 0, 1);
      if (cancel)
        {
          mem_free (stream_name);
          return (NULL);
        }
      stream = fopen (stream_name, "w");
      if (stream == NULL)
        {
          status_mesg (wrong_name, press_enter);
          (void)wgetch (win[STA].p);
        }
    }
  mem_free (stream_name);

  return (stream);
}

/*
 * Travel through each occurence of an item, and execute the given callback
 * (mainly used to export data).
 */
static void
foreach_date_dump (const long date_end, struct rpt *rpt, llist_t *exc,
                   long item_first_date, long item_dur, char *item_mesg,
                   cb_dump_t cb_dump, FILE *stream)
{
  long date, item_time;
  struct tm lt;
  time_t t;

  t = item_first_date;
  lt = *localtime (&t);
  lt.tm_hour = lt.tm_min = lt.tm_sec = 0;
  lt.tm_isdst = -1;
  date = mktime (&lt);
  item_time = item_first_date - date;

  while (date <= date_end && date <= rpt->until)
    {
      if (recur_item_inday (item_first_date, exc, rpt->type, rpt->freq,
                            rpt->until, date))
        {
          (*cb_dump)(stream, date + item_time, item_dur, item_mesg);
        }
      switch (rpt->type)
        {
        case RECUR_DAILY:
          date = date_sec_change (date, 0, rpt->freq);
          break;
        case RECUR_WEEKLY:
          date = date_sec_change (date, 0, rpt->freq * WEEKINDAYS);
          break;
        case RECUR_MONTHLY:
          date = date_sec_change (date, rpt->freq, 0);
          break;
        case RECUR_YEARLY:
          date = date_sec_change (date, rpt->freq * 12, 0);
          break;
        default:
          EXIT (_("incoherent repetition type"));
          /* NOTREACHED */
          break;
        }
    }
}

/* iCal alarm notification. */
static void
ical_export_valarm (FILE *stream)
{
  (void)fprintf (stream, "BEGIN:VALARM\n");
  pthread_mutex_lock (&nbar.mutex);
  (void)fprintf (stream, "TRIGGER:-P%dS\n", nbar.cntdwn);
  pthread_mutex_unlock (&nbar.mutex);
  (void)fprintf (stream, "ACTION:DISPLAY\n");
  (void)fprintf (stream, "END:VALARM\n");
}

/* Export header. */
static void
ical_export_header (FILE *stream)
{
  (void)fprintf (stream, "BEGIN:VCALENDAR\n");
  (void)fprintf (stream, "PRODID:-//calcurse//NONSGML v%s//EN\n", VERSION);
  (void)fprintf (stream, "VERSION:2.0\n");
}

static void
pcal_export_header (FILE *stream)
{
  (void)fprintf (stream, "# calcurse pcal export\n");
  (void)fprintf (stream, "\n# =======\n# options\n# =======\n");
  (void)fprintf (stream, "opt -A -K -l -m -F %s\n",
                 calendar_week_begins_on_monday () ?
                 "Monday" : "Sunday");
  (void)fprintf (stream, "# Display week number (i.e. 1-52) on every Monday\n");
  (void)fprintf (stream, "all monday in all  %s %%w\n", _("Week"));
  (void)fprintf (stream, "\n");
}

/* Export footer. */
static void
ical_export_footer (FILE *stream)
{
  (void)fprintf (stream, "END:VCALENDAR\n");
}

static void
pcal_export_footer (FILE *stream)
{
}

/* Export recurrent events. */
static void
ical_export_recur_events (FILE *stream)
{
  llist_item_t *i, *j;
  char ical_date[BUFSIZ];

  LLIST_FOREACH (&recur_elist, i)
    {
      struct recur_event *rev = LLIST_GET_DATA (i);
      date_sec2date_fmt (rev->day, ICALDATEFMT, ical_date);
      (void)fprintf (stream, "BEGIN:VEVENT\n");
      (void)fprintf (stream, "DTSTART:%s\n", ical_date);
      (void)fprintf (stream, "RRULE:FREQ=%s;INTERVAL=%d",
                     ical_recur_type[rev->rpt->type], rev->rpt->freq);

      if (rev->rpt->until != 0)
        {
          date_sec2date_fmt (rev->rpt->until, ICALDATEFMT, ical_date);
          (void)fprintf (stream, ";UNTIL=%s\n", ical_date);
        }
      else
        (void)fprintf (stream, "\n");

      if (LLIST_FIRST (&rev->exc))
        {
          (void)fprintf (stream, "EXDATE:");
          LLIST_FOREACH (&rev->exc, j)
            {
              struct excp *exc = LLIST_GET_DATA (j);
              date_sec2date_fmt (exc->st, ICALDATEFMT, ical_date);
              (void)fprintf (stream, "%s", ical_date);
              if (LLIST_NEXT (j))
                (void)fprintf (stream, ",");
              else
                (void)fprintf (stream, "\n");
            }
        }

      (void)fprintf (stream, "SUMMARY:%s\n", rev->mesg);
      (void)fprintf (stream, "END:VEVENT\n");
    }
}

/* Format and dump event data to a pcal formatted file. */
static void
pcal_dump_event (FILE *stream, long event_date, long event_dur,
                 char *event_mesg)
{
  char pcal_date[BUFSIZ];

  date_sec2date_fmt (event_date, "%b %d", pcal_date);
  (void)fprintf (stream, "%s  %s\n", pcal_date, event_mesg);
}

/* Format and dump appointment data to a pcal formatted file. */
static void
pcal_dump_apoint (FILE *stream, long apoint_date, long apoint_dur,
                  char *apoint_mesg)
{
  char pcal_date[BUFSIZ], pcal_beg[BUFSIZ], pcal_end[BUFSIZ];

  date_sec2date_fmt (apoint_date, "%b %d", pcal_date);
  date_sec2date_fmt (apoint_date, "%R", pcal_beg);
  date_sec2date_fmt (apoint_date + apoint_dur, "%R", pcal_end);
  (void)fprintf (stream, "%s  ", pcal_date);
  (void)fprintf (stream, "(%s -> %s) %s\n", pcal_beg, pcal_end, apoint_mesg);
}

static void
pcal_export_recur_events (FILE *stream)
{
  llist_item_t *i;
  char pcal_date[BUFSIZ];

  (void)fprintf (stream, "\n# =============");
  (void)fprintf (stream, "\n# Recur. Events");
  (void)fprintf (stream, "\n# =============\n");
  (void)fprintf (stream,
                 "# (pcal does not support from..until dates specification\n");

  LLIST_FOREACH (&recur_elist, i)
    {
      struct recur_event *rev = LLIST_GET_DATA (i);
      if (rev->rpt->until == 0 && rev->rpt->freq == 1)
        {
          switch (rev->rpt->type)
            {
            case RECUR_DAILY:
              date_sec2date_fmt (rev->day, "%b %d", pcal_date);
              (void)fprintf (stream, "all day on_or_after %s  %s\n",
                             pcal_date, rev->mesg);
              break;
            case RECUR_WEEKLY:
              date_sec2date_fmt (rev->day, "%a", pcal_date);
              (void)fprintf (stream, "all %s on_or_after ", pcal_date);
              date_sec2date_fmt (rev->day, "%b %d", pcal_date);
              (void)fprintf (stream, "%s  %s\n", pcal_date, rev->mesg);
              break;
            case RECUR_MONTHLY:
              date_sec2date_fmt (rev->day, "%d", pcal_date);
              (void)fprintf (stream, "day on all %s  %s\n", pcal_date,
                             rev->mesg);
              break;
            case RECUR_YEARLY:
              date_sec2date_fmt (rev->day, "%b %d", pcal_date);
              (void)fprintf (stream, "%s  %s\n", pcal_date, rev->mesg);
              break;
            default:
              EXIT (_("incoherent repetition type"));
            }
        }
      else
        {
          const long YEAR_START = calendar_start_of_year ();
          const long YEAR_END = calendar_end_of_year ();

          if (rev->day < YEAR_END && rev->day > YEAR_START)
            foreach_date_dump (YEAR_END, rev->rpt, &rev->exc, rev->day, 0,
                               rev->mesg, (cb_dump_t) pcal_dump_event, stream);
        }
    }
}

/* Export events. */
static void
ical_export_events (FILE *stream)
{
  llist_item_t *i;
  char ical_date[BUFSIZ];

  LLIST_FOREACH (&eventlist, i)
    {
      struct event *ev = LLIST_TS_GET_DATA (i);
      date_sec2date_fmt (ev->day, ICALDATEFMT, ical_date);
      (void)fprintf (stream, "BEGIN:VEVENT\n");
      (void)fprintf (stream, "DTSTART:%s\n", ical_date);
      (void)fprintf (stream, "SUMMARY:%s\n", ev->mesg);
      (void)fprintf (stream, "END:VEVENT\n");
    }
}

static void
pcal_export_events (FILE *stream)
{
  llist_item_t *i;

  (void)fprintf (stream, "\n# ======\n# Events\n# ======\n");
  LLIST_FOREACH (&eventlist, i)
    {
      struct event *ev = LLIST_TS_GET_DATA (i);
      pcal_dump_event (stream, ev->day, 0, ev->mesg);
    }
  (void)fprintf (stream, "\n");
}

/* Export recurrent appointments. */
static void
ical_export_recur_apoints (FILE *stream)
{
  llist_item_t *i, *j;
  char ical_datetime[BUFSIZ];
  char ical_date[BUFSIZ];

  LLIST_TS_LOCK (&recur_alist_p);
  LLIST_TS_FOREACH (&recur_alist_p, i)
    {
      struct recur_apoint *rapt = LLIST_TS_GET_DATA (i);

      date_sec2date_fmt (rapt->start, ICALDATETIMEFMT, ical_datetime);
      (void)fprintf (stream, "BEGIN:VEVENT\n");
      (void)fprintf (stream, "DTSTART:%s\n", ical_datetime);
      (void)fprintf (stream, "DURATION:PT0H0M%ldS\n", rapt->dur);
      (void)fprintf (stream, "RRULE:FREQ=%s;INTERVAL=%d",
                     ical_recur_type[rapt->rpt->type], rapt->rpt->freq);

      if (rapt->rpt->until != 0)
        {
          date_sec2date_fmt (rapt->rpt->until + HOURINSEC, ICALDATEFMT,
                             ical_date);
          (void)fprintf (stream, ";UNTIL=%s\n", ical_date);
        }
      else
        (void)fprintf (stream, "\n");

      if (LLIST_FIRST (&rapt->exc))
        {
          (void)fprintf (stream, "EXDATE:");
          LLIST_FOREACH (&rapt->exc, j)
            {
              struct excp *exc = LLIST_GET_DATA (j);
              date_sec2date_fmt (exc->st, ICALDATEFMT, ical_date);
              (void)fprintf (stream, "%s", ical_date);
              if (LLIST_NEXT (j))
                (void)fprintf (stream, ",");
              else
                (void)fprintf (stream, "\n");
            }
        }

      (void)fprintf (stream, "SUMMARY:%s\n", rapt->mesg);
      if (rapt->state & APOINT_NOTIFY)
        ical_export_valarm (stream);
      (void)fprintf (stream, "END:VEVENT\n");
    }
  LLIST_TS_UNLOCK (&recur_alist_p);
}

static void
pcal_export_recur_apoints (FILE *stream)
{
  llist_item_t *i;
  char pcal_date[BUFSIZ], pcal_beg[BUFSIZ], pcal_end[BUFSIZ];

  (void)fprintf (stream, "\n# ==============");
  (void)fprintf (stream, "\n# Recur. Apoints");
  (void)fprintf (stream, "\n# ==============\n");
  (void)fprintf (stream,
                 "# (pcal does not support from..until dates specification\n");

  LLIST_TS_FOREACH (&recur_alist_p, i)
    {
      struct recur_apoint *rapt = LLIST_TS_GET_DATA (i);

      if (rapt->rpt->until == 0 && rapt->rpt->freq == 1)
        {
          date_sec2date_fmt (rapt->start, "%R", pcal_beg);
          date_sec2date_fmt (rapt->start + rapt->dur, "%R", pcal_end);
          switch (rapt->rpt->type)
            {
            case RECUR_DAILY:
              date_sec2date_fmt (rapt->start, "%b %d", pcal_date);
              (void)fprintf (stream, "all day on_or_after %s  (%s -> %s) %s\n",
                             pcal_date, pcal_beg, pcal_end, rapt->mesg);
              break;
            case RECUR_WEEKLY:
              date_sec2date_fmt (rapt->start, "%a", pcal_date);
              (void)fprintf (stream, "all %s on_or_after ", pcal_date);
              date_sec2date_fmt (rapt->start, "%b %d", pcal_date);
              (void)fprintf (stream, "%s  (%s -> %s) %s\n", pcal_date,
                             pcal_beg, pcal_end, rapt->mesg);
              break;
            case RECUR_MONTHLY:
              date_sec2date_fmt (rapt->start, "%d", pcal_date);
              (void)fprintf (stream, "day on all %s  (%s -> %s) %s\n",
                             pcal_date, pcal_beg, pcal_end, rapt->mesg);
              break;
            case RECUR_YEARLY:
              date_sec2date_fmt (rapt->start, "%b %d", pcal_date);
              (void)fprintf (stream, "%s  (%s -> %s) %s\n", pcal_date,
                             pcal_beg, pcal_end, rapt->mesg);
              break;
            default:
              EXIT (_("incoherent repetition type"));
            }
        }
      else
        {
          const long YEAR_START = calendar_start_of_year ();
          const long YEAR_END = calendar_end_of_year ();

          if (rapt->start < YEAR_END && rapt->start > YEAR_START)
            foreach_date_dump (YEAR_END, rapt->rpt, &rapt->exc, rapt->start,
                               rapt->dur, rapt->mesg,
                               (cb_dump_t)pcal_dump_apoint, stream);
        }
    }
}

/* Export appointments. */
static void
ical_export_apoints (FILE *stream)
{
  llist_item_t *i;
  char ical_datetime[BUFSIZ];

  LLIST_TS_LOCK (&alist_p);
  LLIST_TS_FOREACH (&alist_p, i)
    {
      struct apoint *apt = LLIST_TS_GET_DATA (i);
      date_sec2date_fmt (apt->start, ICALDATETIMEFMT, ical_datetime);
      (void)fprintf (stream, "BEGIN:VEVENT\n");
      (void)fprintf (stream, "DTSTART:%s\n", ical_datetime);
      (void)fprintf (stream, "DURATION:P%ldDT%ldH%ldM%ldS\n",
                     apt->dur / DAYINSEC,
                     (apt->dur / HOURINSEC) % 24,
                     (apt->dur / MININSEC) % 60,
                     apt->dur % MININSEC);
      (void)fprintf (stream, "SUMMARY:%s\n", apt->mesg);
      if (apt->state & APOINT_NOTIFY)
        ical_export_valarm (stream);
      (void)fprintf (stream, "END:VEVENT\n");
    }
  LLIST_TS_UNLOCK (&alist_p);
}

static void
pcal_export_apoints (FILE *stream)
{
  llist_item_t *i;

  (void)fprintf (stream, "\n# ============\n# Appointments\n# ============\n");
  LLIST_TS_LOCK (&alist_p);
  LLIST_TS_FOREACH (&alist_p, i)
    {
      struct apoint *apt = LLIST_TS_GET_DATA (i);
      pcal_dump_apoint (stream, apt->start, apt->dur, apt->mesg);
    }
  LLIST_TS_UNLOCK (&alist_p);
  (void)fprintf (stream, "\n");
}

/* Export todo items. */
static void
ical_export_todo (FILE *stream)
{
  llist_item_t *i;

  LLIST_FOREACH (&todolist, i)
    {
      struct todo *todo = LLIST_TS_GET_DATA (i);
      if (todo->id < 0)  /* completed items */
        continue;

      (void)fprintf (stream, "BEGIN:VTODO\n");
      (void)fprintf (stream, "PRIORITY:%d\n", todo->id);
      (void)fprintf (stream, "SUMMARY:%s\n", todo->mesg);
      (void)fprintf (stream, "END:VTODO\n");
    }
}

static void
pcal_export_todo (FILE *stream)
{
  llist_item_t *i;

  (void)fprintf (stream, "#\n# Todos\n#\n");
  LLIST_FOREACH (&todolist, i)
    {
      struct todo *todo = LLIST_TS_GET_DATA (i);
      if (todo->id < 0)  /* completed items */
        continue;

      (void)fprintf (stream, "note all  ");
      (void)fprintf (stream, "%d. %s\n", todo->id, todo->mesg);
    }
  (void)fprintf (stream, "\n");
}

/* Append a line to a file. */
unsigned
io_fprintln (const char *fname, const char *fmt, ...)
{
  FILE *fp;
  va_list ap;
  char buf[BUFSIZ];
  int ret;

  fp = fopen (fname, "a");
  RETVAL_IF (!fp, 0, _("Failed to open \"%s\", - %s\n"),
             fname, strerror (errno));

  va_start (ap, fmt);
  ret = vsnprintf (buf, sizeof buf, fmt, ap);
  RETVAL_IF (ret < 0, 0, _("Failed to build message\n"));
  va_end (ap);

  ret = fprintf (fp, "%s", buf);
  RETVAL_IF (ret < 0, 0, _("Failed to print message \"%s\"\n"), buf);

  ret = fclose (fp);
  RETVAL_IF (ret != 0, 0, _("Failed to close \"%s\" - %s\n"),
             fname, strerror (errno));

  return 1;
}

/*
 * Initialization of data paths. The cfile argument is the variable
 * which contains the calendar file. If none is given, then the default
 * one (~/.calcurse/apts) is taken. If the one given does not exist, it
 * is created.
 * The datadir argument can be use to specify an alternative data root dir.
 */
void
io_init (char *cfile, char *datadir)
{
  FILE *data_file;
  char *home;
  char apts_file[BUFSIZ] = "";
  int ch;

  if (datadir != NULL)
    {
      home = datadir;
      (void)snprintf (path_dir, BUFSIZ, "%s", home);
      (void)snprintf (path_todo, BUFSIZ, "%s/" TODO_PATH_NAME, home);
      (void)snprintf (path_conf, BUFSIZ, "%s/" CONF_PATH_NAME, home);
      (void)snprintf (path_notes, BUFSIZ, "%s/" NOTES_DIR_NAME, home);
      (void)snprintf (path_apts, BUFSIZ, "%s/" APTS_PATH_NAME, home);
      (void)snprintf (path_keys, BUFSIZ, "%s/" KEYS_PATH_NAME, home);
      (void)snprintf (path_cpid, BUFSIZ, "%s/" CPID_PATH_NAME, home);
      (void)snprintf (path_dpid, BUFSIZ, "%s/" DPID_PATH_NAME, home);
      (void)snprintf (path_dmon_log, BUFSIZ, "%s/" DLOG_PATH_NAME, home);
    }
  else
    {
      home = getenv ("HOME");
      if (home == NULL)
        {
          home = ".";
        }
      (void)snprintf (path_dir, BUFSIZ, "%s/" DIR_NAME, home);
      (void)snprintf (path_todo, BUFSIZ, "%s/" TODO_PATH, home);
      (void)snprintf (path_conf, BUFSIZ, "%s/" CONF_PATH, home);
      (void)snprintf (path_keys, BUFSIZ, "%s/" KEYS_PATH, home);
      (void)snprintf (path_cpid, BUFSIZ, "%s/" CPID_PATH, home);
      (void)snprintf (path_dpid, BUFSIZ, "%s/" DPID_PATH, home);
      (void)snprintf (path_dmon_log, BUFSIZ, "%s/" DLOG_PATH, home);
      (void)snprintf (path_notes, BUFSIZ, "%s/" NOTES_DIR, home);
      if (cfile == NULL)
        {
          (void)snprintf (path_apts, BUFSIZ, "%s/" APTS_PATH, home);
        }
      else
        {
          (void)snprintf (apts_file, BUFSIZ, "%s", cfile);
          (void)strncpy (path_apts, apts_file, BUFSIZ);
          /* check if the file exists, otherwise create it */
          data_file = fopen (path_apts, "r");
          if (data_file == NULL)
            {
              printf (_("%s does not exist, create it now [y or n] ? "),
                      path_apts);
              ch = getchar ();
              switch (ch)
                {
                case 'N':
                case 'n':
                  printf (_("aborting...\n"));
                  exit_calcurse (EXIT_FAILURE);
                  break;

                case 'Y':
                case 'y':
                  data_file = fopen (path_apts, "w");
                  if (data_file == NULL)
                    {
                      perror (path_apts);
                      exit_calcurse (EXIT_FAILURE);
                    }
                  else
                    {
                      printf (_("%s successfully created\n"), path_apts);
                      printf (_("starting interactive mode...\n"));
                    }
                  break;

                default:
                  printf (_("aborting...\n"));
                  exit_calcurse (EXIT_FAILURE);
                  break;
                }
            }
          file_close (data_file, __FILE_POS__);
        }
    }
}

void
io_extract_data (char *dst_data, const char *org, int len)
{
  int i;

  for (i = 0; i < len - 1; i++)
    {
      if (*org == '\n' || *org == '\0')
        break;
      *dst_data++ = *org++;
    }
  *dst_data = '\0';
}

void
display_mark (void)
{
  const int DISPLAY_TIME = 1;
  WINDOW *mwin;

  mwin = newwin (1, 2, 1, col - 3);

  custom_apply_attr (mwin, ATTR_HIGHEST);
  mvwprintw (mwin, 0, 0, "**");
  wins_wrefresh (mwin);
  sleep (DISPLAY_TIME);
  mvwprintw (mwin, 0, 0, "  ");
  wins_wrefresh (mwin);
  delwin (mwin);
  wins_doupdate ();
}

static pthread_mutex_t io_save_mutex = PTHREAD_MUTEX_INITIALIZER;

/* Save the user configuration. */
unsigned
io_save_conf (struct conf *conf)
{
  char *config_txt =
    "#\n"
    "# Calcurse configuration file\n#\n"
    "# This file sets the configuration options used by Calcurse. These\n"
    "# options are usually set from within Calcurse. A line beginning with \n"
    "# a space or tab is considered to be a continuation of the previous "
    "line.\n"
    "# For a variable to be unset its value must be blank.\n"
    "# To set a variable to the empty string its value should be \"\".\n"
    "# Lines beginning with \"#\" are comments, and ignored by Calcurse.\n";
  char theme_name[BUFSIZ];
  FILE *fp;

  if ((fp = fopen (path_conf, "w")) == NULL)
    return 0;

  custom_color_theme_name (theme_name);

  (void)fprintf (fp, "%s\n", config_txt);

  (void)fprintf (fp, "# If this option is set to yes, "
                 "automatic save is done when quitting\n");
  (void)fprintf (fp, "auto_save=\n");
  (void)fprintf (fp, "%s\n", (conf->auto_save) ? "yes" : "no");

  (void)fprintf (fp, "\n# If not null, perform automatic saves every "
                 "'periodic_save' minutes\n");
  (void)fprintf (fp, "periodic_save=\n");
  (void)fprintf (fp, "%d\n", conf->periodic_save);

  (void)fprintf (fp, "\n# If this option is set to yes, "
                 "confirmation is required before quitting\n");
  (void)fprintf (fp, "confirm_quit=\n");
  (void)fprintf (fp, "%s\n", (conf->confirm_quit) ? "yes" : "no");

  (void)fprintf (fp, "\n# If this option is set to yes, "
                 "confirmation is required before deleting an event\n");
  (void)fprintf (fp, "confirm_delete=\n");
  (void)fprintf (fp, "%s\n", (conf->confirm_delete) ? "yes" : "no");

  (void)fprintf (fp, "\n# If this option is set to yes, "
                 "messages about loaded and saved data will not be displayed\n");
  (void)fprintf (fp, "skip_system_dialogs=\n");
  (void)fprintf (fp, "%s\n", (conf->skip_system_dialogs) ? "yes" : "no");

  (void)fprintf (fp,
                 "\n# If this option is set to yes, progress bar appearing "
                 "when saving data will not be displayed\n");
  (void)fprintf (fp, "skip_progress_bar=\n");
  (void)fprintf (fp, "%s\n", (conf->skip_progress_bar) ? "yes" : "no");

  (void)fprintf (fp, "\n# Default calendar view (0)monthly (1)weekly:\n");
  (void)fprintf (fp, "calendar_default_view=\n");
  (void)fprintf (fp, "%d\n", calendar_get_view ());

  (void)fprintf (fp, "\n# If this option is set to yes, "
                 "monday is the first day of the week, else it is sunday\n");
  (void)fprintf (fp, "week_begins_on_monday=\n");
  (void)fprintf (fp, "%s\n",
                 (calendar_week_begins_on_monday ())? "yes" : "no");

  (void)fprintf (fp, "\n# This is the color theme used for menus :\n");
  (void)fprintf (fp, "color-theme=\n");
  (void)fprintf (fp, "%s\n", theme_name);

  (void)fprintf (fp, "\n# This is the layout of the calendar :\n");
  (void)fprintf (fp, "layout=\n");
  (void)fprintf (fp, "%d\n", wins_layout ());

  (void)fprintf (fp, "\n# Width (in percentage, 0 being minimun width) "
                 "of the side bar :\n");
  (void)fprintf (fp, "side-bar_width=\n");
  (void)fprintf (fp, "%d\n", wins_sbar_wperc ());

  if (ui_mode == UI_CURSES)
    pthread_mutex_lock (&nbar.mutex);
  (void)fprintf (fp,
                 "\n# If this option is set to yes, "
                 "notify-bar will be displayed :\n");
  (void)fprintf (fp, "notify-bar_show=\n");
  (void)fprintf (fp, "%s\n", (nbar.show) ? "yes" : "no");

  (void)fprintf (fp,
                 "\n# Format of the date to be displayed inside notify-bar :\n");
  (void)fprintf (fp, "notify-bar_date=\n");
  (void)fprintf (fp, "%s\n", nbar.datefmt);

  (void)fprintf (fp,
                 "\n# Format of the time to be displayed inside notify-bar :\n");
  (void)fprintf (fp, "notify-bar_clock=\n");
  (void)fprintf (fp, "%s\n", nbar.timefmt);

  (void)fprintf (fp,
                 "\n# Warn user if he has an appointment within next "
                 "'notify-bar_warning' seconds :\n");
  (void)fprintf (fp, "notify-bar_warning=\n");
  (void)fprintf (fp, "%d\n", nbar.cntdwn);

  (void)fprintf (fp, "\n# Command used to notify user of "
                 "an upcoming appointment :\n");
  (void)fprintf (fp, "notify-bar_command=\n");
  (void)fprintf (fp, "%s\n", nbar.cmd);

  (void)fprintf (fp, "\n# Format of the date to be displayed "
                 "in non-interactive mode :\n");
  (void)fprintf (fp, "output_datefmt=\n");
  (void)fprintf (fp, "%s\n", conf->output_datefmt);

  (void)fprintf (fp, "\n# Format to be used when entering a date "
                 "(1)mm/dd/yyyy (2)dd/mm/yyyy (3)yyyy/mm/dd) "
                 "(4)yyyy-mm-dd:\n");
  (void)fprintf (fp, "input_datefmt=\n");
  (void)fprintf (fp, "%d\n", conf->input_datefmt);

  if (ui_mode == UI_CURSES)
    pthread_mutex_unlock (&nbar.mutex);

  (void)fprintf (fp, "\n# If this option is set to yes, "
                 "calcurse will run in background to get notifications "
                 "after exiting\n");
  (void)fprintf (fp, "notify-daemon_enable=\n");
  (void)fprintf (fp, "%s\n", dmon.enable ? "yes" : "no");

    (void)fprintf (fp, "\n# If this option is set to yes, "
                   "activity will be logged when running in background\n");
  (void)fprintf (fp, "notify-daemon_log=\n");
  (void)fprintf (fp, "%s\n", dmon.log ? "yes" : "no");

  file_close (fp, __FILE_POS__);

  return 1;
}

/*
 * Save the apts data file, which contains the
 * appointments first, and then the events.
 * Recursive items are written first.
 */
unsigned
io_save_apts (void)
{
  llist_item_t *i;
  FILE *fp;

  if ((fp = fopen (path_apts, "w")) == NULL)
    return 0;

  recur_save_data (fp);

  if (ui_mode == UI_CURSES)
    LLIST_TS_LOCK (&alist_p);
  LLIST_TS_FOREACH (&alist_p, i)
    {
      struct apoint *apt = LLIST_TS_GET_DATA (i);
      apoint_write (apt, fp);
    }
  if (ui_mode == UI_CURSES)
    LLIST_TS_UNLOCK (&alist_p);

  LLIST_FOREACH (&eventlist, i)
    {
      struct event *ev = LLIST_TS_GET_DATA (i);
      event_write (ev, fp);
    }
  file_close (fp, __FILE_POS__);

  return 1;
}

/* Save the todo data file. */
unsigned
io_save_todo (void)
{
  llist_item_t *i;
  FILE *fp;

  if ((fp = fopen (path_todo, "w")) == NULL)
    return 0;

  LLIST_FOREACH (&todolist, i)
    {
      struct todo *todo = LLIST_TS_GET_DATA (i);
      if (todo->note)
        (void)fprintf (fp, "[%d]>%s %s\n", todo->id, todo->note, todo->mesg);
      else
        (void)fprintf (fp, "[%d] %s\n", todo->id, todo->mesg);
    }
  file_close (fp, __FILE_POS__);

  return 1;
}

/* Save user-defined keys */
unsigned
io_save_keys (void)
{
  FILE *fp;

  if ((fp = fopen (path_keys, "w")) == NULL)
    return 0;

  keys_save_bindings (fp);
  file_close (fp, __FILE_POS__);

  return 1;
}

/* Save the calendar data */
void
io_save_cal (struct conf *conf, enum save_display display)
{
  char *access_pb = _("Problems accessing data file ...");
  char *save_success = _("The data files were successfully saved");
  char *enter = _("Press [ENTER] to continue");
  int show_bar;

  pthread_mutex_lock (&io_save_mutex);

  show_bar = 0;
  if (ui_mode == UI_CURSES && display == IO_SAVE_DISPLAY_BAR
      && !conf->skip_progress_bar)
    show_bar = 1;
  else if (ui_mode == UI_CURSES && display == IO_SAVE_DISPLAY_MARK)
    display_mark ();

  if (show_bar)
    progress_bar (PROGRESS_BAR_SAVE, PROGRESS_BAR_CONF);
  if (!io_save_conf (conf))
    ERROR_MSG ("%s", access_pb);

  if (show_bar)
    progress_bar (PROGRESS_BAR_SAVE, PROGRESS_BAR_TODO);
  if (!io_save_todo ())
    ERROR_MSG ("%s", access_pb);

  if (show_bar)
    progress_bar (PROGRESS_BAR_SAVE, PROGRESS_BAR_APTS);
  if (!io_save_apts ())
    ERROR_MSG ("%s", access_pb);

  if (show_bar)
    progress_bar (PROGRESS_BAR_SAVE, PROGRESS_BAR_KEYS);
  if (!io_save_keys ())
    ERROR_MSG ("%s", access_pb);

  /* Print a message telling data were saved */
  if (ui_mode == UI_CURSES && !conf->skip_system_dialogs
      && display != IO_SAVE_DISPLAY_MARK)
    {
      status_mesg (save_success, enter);
      (void)wgetch (win[STA].p);
    }

  pthread_mutex_unlock (&io_save_mutex);
}

/*
 * Check what type of data is written in the appointment file,
 * and then load either: a new appointment, a new event, or a new
 * recursive item (which can also be either an event or an appointment).
 */
void
io_load_app (void)
{
  FILE *data_file;
  int c, is_appointment, is_event, is_recursive;
  struct tm start, end, until, *lt;
  llist_t exc;
  time_t t;
  int id = 0;
  int freq;
  char type, state = 0L;
  char note[NOTESIZ + 1], *notep;

  t = time (NULL);
  lt = localtime (&t);
  start = end = until = *lt;

  data_file = fopen (path_apts, "r");
  for (;;)
    {
      LLIST_INIT (&exc);
      is_appointment = is_event = is_recursive = 0;
      c = getc (data_file);
      if (c == EOF)
        break;
      (void)ungetc (c, data_file);

      /* Read the date first: it is common to both events
       * and appointments.
       */
      if (fscanf (data_file, "%u / %u / %u ",
                  &start.tm_mon, &start.tm_mday, &start.tm_year) != 3)
        {
          EXIT (_("syntax error in the item date"));
        }

      /* Read the next character : if it is an '@' then we have
       * an appointment, else if it is an '[' we have en event.
       */
      c = getc (data_file);

      if (c == '@')
        is_appointment = 1;
      else if (c == '[')
        is_event = 1;
      else
        {
          EXIT (_("no event nor appointment found"));
        }
      (void)ungetc (c, data_file);

      /* Read the remaining informations. */
      if (is_appointment)
        {
          fscanf (data_file, "@ %u : %u -> %u / %u / %u @ %u : %u ",
                  &start.tm_hour, &start.tm_min,
                  &end.tm_mon, &end.tm_mday, &end.tm_year,
                  &end.tm_hour, &end.tm_min);
        }
      else if (is_event)
        {
          fscanf (data_file, "[%d] ", &id);
        }
      else
        {
          EXIT (_("wrong format in the appointment or event"));
          /* NOTREACHED */
        }

      /* Check if we have a recursive item. */
      c = getc (data_file);

      if (c == '{')
        {
          (void)ungetc (c, data_file);
          is_recursive = 1;
          fscanf (data_file, "{ %d%c ", &freq, &type);

          c = getc (data_file);
          if (c == '}')
            {			/* endless recurrent item */
              (void)ungetc (c, data_file);
              fscanf (data_file, "} ");
              until.tm_year = 0;
            }
          else if (c == '-')
            {
              (void)ungetc (c, data_file);
              fscanf (data_file, " -> %u / %u / %u ",
                      &until.tm_mon, &until.tm_mday, &until.tm_year);
              c = getc (data_file);
              if (c == '!')
                {
                  (void)ungetc (c, data_file);
                  recur_exc_scan (&exc, data_file);
                  c = getc (data_file);
                }
              else
                {
                  (void)ungetc (c, data_file);
                  fscanf (data_file, "} ");
                }
            }
          else if (c == '!')
            {			// endless item with exceptions
              (void)ungetc (c, data_file);
              recur_exc_scan (&exc, data_file);
              c = getc (data_file);
              until.tm_year = 0;
            }
          else
            {
              EXIT (_("wrong format in the appointment or event"));
              /* NOTREACHED */
            }
        }
      else
        (void)ungetc (c, data_file);

      /* Check if a note is attached to the item. */
      c = getc (data_file);
      if (c == '>')
        {
          (void)fgets (note, NOTESIZ + 1, data_file);
          note[NOTESIZ] = '\0';
          notep = note;
          getc (data_file);
        }
      else
        {
          notep = NULL;
          (void)ungetc (c, data_file);
        }

      /*
       * Last: read the item description and load it into its
       * corresponding linked list, depending on the item type.
       */
      if (is_appointment)
        {
          c = getc (data_file);
          if (c == '!')
            {
              (void)ungetc (c, data_file);
              fscanf (data_file, " ! ");
              state |= APOINT_NOTIFY;
            }
          else
            {
              (void)ungetc (c, data_file);
              fscanf (data_file, " | ");
              state = 0L;
            }
          if (is_recursive)
            {
              recur_apoint_scan (data_file, start, end,
                                 type, freq, until, notep, &exc, state);
            }
          else
            {
              apoint_scan (data_file, start, end, state, notep);
            }
        }
      else if (is_event)
        {
          if (is_recursive)
            {
              recur_event_scan (data_file, start, id, type,
                                freq, until, notep, &exc);
            }
          else
            {
              event_scan (data_file, start, id, notep);
            }
        }
      else
        {
          EXIT (_("wrong format in the appointment or event"));
          /* NOTREACHED */
        }
    }
  file_close (data_file, __FILE_POS__);
}

/* Load the todo data */
void
io_load_todo (void)
{
  FILE *data_file;
  char *mesg_line1 = _("Failed to open todo file");
  char *mesg_line2 = _("Press [ENTER] to continue");
  char *newline;
  int nb_tod = 0;
  int c, id;
  char buf[BUFSIZ], e_todo[BUFSIZ], note[NOTESIZ + 1];

  data_file = fopen (path_todo, "r");
  if (data_file == NULL)
    {
      status_mesg (mesg_line1, mesg_line2);
      (void)wgetch (win[STA].p);
    }
  for (;;)
    {
      c = getc (data_file);
      if (c == EOF)
        {
          break;
        }
      else if (c == '[')
        {			/* new style with id */
          fscanf (data_file, "%d]", &id);
        }
      else
        {
          id = 9;
          (void)ungetc (c, data_file);
        }
      /* Now read the attached note, if any. */
      c = getc (data_file);
      if (c == '>')
        {
          (void)fgets (note, NOTESIZ + 1, data_file);
          note[NOTESIZ] = '\0';
          getc (data_file);
        }
      else
        note[0] = '\0';
      /* Then read todo description. */
      (void)fgets (buf, sizeof buf, data_file);
      newline = strchr (buf, '\n');
      if (newline)
        *newline = '\0';
      io_extract_data (e_todo, buf, sizeof buf);
      todo_add (e_todo, id, note);
      ++nb_tod;
    }
  file_close (data_file, __FILE_POS__);
  todo_set_nb (nb_tod);
}

static void
load_keys_ht_getkey (struct ht_keybindings_s *data, char **key, int *len)
{
  *key = data->label;
  *len = strlen (data->label);
}

static int
load_keys_ht_compare (struct ht_keybindings_s *data1,
                      struct ht_keybindings_s *data2)
{
  const int KEYLEN = strlen (data1->label);

  if (strlen (data2->label) == KEYLEN
      && !memcmp (data1->label, data2->label, KEYLEN))
    return 0;
  else
    return 1;
}

/*
 * isblank(3) is protected by the __BSD_VISIBLE macro and this fails to be
 * visible in some specific cases. Thus replace it by the following is_blank()
 * function.
 */
static int is_blank (int c)
{
  return c == ' ' || c == '\t';
}

/*
 * Load user-definable keys from file.
 * A hash table is used to speed up loading process in avoiding string
 * comparisons.
 * A log file is also built in case some errors were found in the key
 * configuration file.
 */
void
io_load_keys (char *pager)
{
  struct ht_keybindings_s keys[NBKEYS];
  FILE *keyfp;
  char buf[BUFSIZ];
  struct io_file *log;
  int i, skipped, loaded, line;
  const int MAX_ERRORS = 5;

  keys_init ();

#define HSIZE 256
  HTABLE_HEAD (ht_keybindings, HSIZE, ht_keybindings_s) ht_keys =
    HTABLE_INITIALIZER (&ht_keys);

  HTABLE_GENERATE (ht_keybindings, ht_keybindings_s, load_keys_ht_getkey,
                   load_keys_ht_compare);

  for (i = 0; i < NBKEYS; i++)
    {
      keys[i].key = (enum key)i;
      keys[i].label = keys_get_label ((enum key)i);
      HTABLE_INSERT (ht_keybindings, &ht_keys, &keys[i]);
    }

  keyfp = fopen (path_keys, "r");
  EXIT_IF (keyfp == NULL, _("could not find any key file."));
  log = io_log_init ();
  skipped = loaded = line = 0;
  while (fgets (buf, BUFSIZ, keyfp) != NULL)
    {
      char key_label[BUFSIZ], *p;
      struct ht_keybindings_s *ht_elm, ht_entry;
      const int AWAITED = 1;
      int assigned;

      line++;
      if (skipped > MAX_ERRORS)
        {
          char *too_many =
            _("\nToo many errors while reading configuration file!\n"
              "Please backup your keys file, remove it from directory, "
              "and launch calcurse again.\n");

          io_log_print (log, line, too_many);
          break;
        }
      for (p = buf; is_blank ((int)*p); p++)
        ;
      if (p != buf)
        memmove (buf, p, strlen (p));
      if (buf[0] == '#' || buf[0] == '\n')
        continue;

      if (sscanf (buf, "%s", key_label) != AWAITED)
        {
          skipped++;
          io_log_print (log, line, _("Could not read key label"));
          continue;
        }
      ht_entry.label = key_label;
      p = buf + strlen (key_label) + 1;
      ht_elm = HTABLE_LOOKUP (ht_keybindings, &ht_keys, &ht_entry);
      if (!ht_elm)
        {
          skipped++;
          io_log_print (log, line, _("Key label not recognized"));
          continue;
        }
      assigned = 0;
      for (;;)
        {
          char key_ch[BUFSIZ], tmpbuf[BUFSIZ];

          while (*p == ' ')
            p++;
          (void)strncpy (tmpbuf, p, BUFSIZ);
          if (sscanf (tmpbuf, "%s", key_ch) == AWAITED)
            {
              int ch;

              if ((ch = keys_str2int (key_ch)) < 0)
                {
                  char unknown_key[BUFSIZ];

                  skipped++;
                  (void)snprintf (unknown_key, BUFSIZ,
                                  _("Error reading key: \"%s\""), key_ch);
                  io_log_print (log, line, unknown_key);
                }
              else
                {
                  int used;

                  used = keys_assign_binding (ch, ht_elm->key);
                  if (used)
                    {
                      char already_assigned[BUFSIZ];

                      skipped++;
                      (void)snprintf (already_assigned, BUFSIZ,
                                _("\"%s\" assigned multiple times!"), key_ch);
                      io_log_print (log, line, already_assigned);
                    }
                  else
                    assigned++;
                }
              p += strlen (key_ch) + 1;
            }
          else
            {
              if (assigned)
                loaded++;
              break;
            }
        }
    }
  file_close (keyfp, __FILE_POS__);
  file_close (log->fd, __FILE_POS__);
  if (skipped > 0)
    {
      char *view_log =
        _("There were some errors when loading keys file, see log file ?");

      io_log_display (log, view_log, pager);
    }
  io_log_free (log);
  EXIT_IF (skipped > MAX_ERRORS,
           _("Too many errors while reading keys file, aborting..."));
  if (loaded < NBKEYS)
    keys_fill_missing ();
  if (keys_check_missing_bindings ())
    WARN_MSG (_("Some actions do not have any associated key bindings!"));
#undef HSIZE
}

void
io_check_dir (char *dir, int *missing)
{
  errno = 0;
  if (mkdir (dir, 0700) != 0)
    {
      if (errno != EEXIST)
        {
          (void)fprintf (stderr, _("FATAL ERROR: could not create %s: %s\n"),
                         dir, strerror (errno));
          exit_calcurse (EXIT_FAILURE);
        }
    }
  else
    {
      if (missing)
        (*missing)++;
    }
}

unsigned
io_file_exist (char *file)
{
  FILE *fd;

  if (!file)
    return 0;

  if ((fd = fopen (file, "r")) == NULL)
    return 0;

  (void)fclose (fd);

  return 1;
}

void
io_check_file (char *file, int *missing)
{
  errno = 0;
  if (!io_file_exist (file))
    {
      FILE *fd;

      if (missing)
        (*missing)++;
      if ((fd = fopen (file, "w")) == NULL)
        {
          (void)fprintf (stderr, _("FATAL ERROR: could not create %s: %s\n"),
                         file, strerror (errno));
          exit_calcurse (EXIT_FAILURE);
        }
      file_close (fd, __FILE_POS__);
    }
}

/*
 * Checks if data files exist. If not, create them.
 * The following structure has to be created:
 *
 *	$HOME/.calcurse/
 *                 |
 *                 +--- notes/
 *                 |___ conf
 *                 |___ keys
 *                 |___ apts
 *                 |___ todo
 */
int
io_check_data_files (void)
{
  int missing, missing_keys;

  missing = missing_keys = 0;
  errno = 0;
  io_check_dir (path_dir, &missing);
  io_check_dir (path_notes, &missing);
  io_check_file (path_todo, &missing);
  io_check_file (path_apts, &missing);
  io_check_file (path_conf, &missing);
  io_check_file (path_keys, &missing_keys);
  if (missing_keys)
    {
      missing++;
      keys_dump_defaults (path_keys);
    }

  return missing;
}

/* Draw the startup screen */
void
io_startup_screen (unsigned skip_dialogs, int no_data_file)
{
  char *welcome_mesg =
    _("Welcome to Calcurse. Missing data files were created.");
  char *data_mesg = _("Data files found. Data will be loaded now.");
  char *enter = _("Press [ENTER] to continue");

  if (no_data_file != 0)
    {
      status_mesg (welcome_mesg, enter);
      (void)wgetch (win[STA].p);
    }
  else if (!skip_dialogs)
    {
      status_mesg (data_mesg, enter);
      (void)wgetch (win[STA].p);
    }
}

/* Export calcurse data. */
void
io_export_data (enum export_type type, struct conf *conf)
{
  FILE *stream;
  char *success = _("The data were successfully exported");
  char *enter = _("Press [ENTER] to continue");

  if (type < IO_EXPORT_ICAL || type >= IO_EXPORT_NBTYPES)
    EXIT (_("unknown export type"));

  stream = 0;
  switch (ui_mode)
    {
    case UI_CMDLINE:
      stream = stdout;
      break;
    case UI_CURSES:
      stream = get_export_stream (type);
      break;
    default:
      EXIT (_("wrong export mode"));
      /* NOTREACHED */
    }

  if (stream == NULL)
    return;

  cb_export_header[type] (stream);

  if (!conf->skip_progress_bar && ui_mode == UI_CURSES)
    progress_bar (PROGRESS_BAR_EXPORT, PROGRESS_BAR_EXPORT_EVENTS);
  cb_export_recur_events[type] (stream);
  cb_export_events[type] (stream);

  if (!conf->skip_progress_bar && ui_mode == UI_CURSES)
    progress_bar (PROGRESS_BAR_EXPORT, PROGRESS_BAR_EXPORT_APOINTS);
  cb_export_recur_apoints[type] (stream);
  cb_export_apoints[type] (stream);

  if (!conf->skip_progress_bar && ui_mode == UI_CURSES)
    progress_bar (PROGRESS_BAR_EXPORT, PROGRESS_BAR_EXPORT_TODO);
  cb_export_todo[type] (stream);

  cb_export_footer[type] (stream);

  if (stream != stdout)
    file_close (stream, __FILE_POS__);

  if (!conf->skip_system_dialogs && ui_mode == UI_CURSES)
    {
      status_mesg (success, enter);
      (void)wgetch (win[STA].p);
    }
}

/* Draws the export format selection bar */
void
io_export_bar (void)
{
  int smlspc, spc;

  smlspc = 2;
  spc = 15;

  custom_apply_attr (win[STA].p, ATTR_HIGHEST);
  mvwprintw (win[STA].p, 0, 2, "Q");
  mvwprintw (win[STA].p, 1, 2, "I");
  mvwprintw (win[STA].p, 0, 2 + spc, "P");
  custom_remove_attr (win[STA].p, ATTR_HIGHEST);

  mvwprintw (win[STA].p, 0, 2 + smlspc, _("Exit"));
  mvwprintw (win[STA].p, 1, 2 + smlspc, _("Ical"));
  mvwprintw (win[STA].p, 0, 2 + spc + smlspc, _("Pcal"));

  wnoutrefresh (win[STA].p);
  wmove (win[STA].p, 0, 0);
  wins_doupdate ();
}

/* Print a header to describe import log report format. */
static void
ical_log_init (FILE *log, float version)
{
  const char *header =
    "+-------------------------------------------------------------------+\n"
    "| Calcurse icalendar import log.                                    |\n"
    "|                                                                   |\n"
    "| Items imported from icalendar file, version %1.1f                   |\n"
    "| Some items could not be imported, they are described hereafter.   |\n"
    "| The log line format is as follows:                                |\n"
    "|                                                                   |\n"
    "|       TYPE [LINE]: DESCRIPTION                                    |\n"
    "|                                                                   |\n"
    "| where:                                                            |\n"
    "|  * TYPE represents the item type ('VEVENT' or 'VTODO')            |\n"
    "|  * LINE is the line in the input stream at which this item begins |\n"
    "|  * DESCRIPTION indicates why the item could not be imported       |\n"
    "+-------------------------------------------------------------------+\n\n";

  if (log)
    (void)fprintf (log, header, version);
}

/*
 * Used to build a report of the import process.
 * The icalendar item for which a problem occurs is mentioned (by giving its
 * first line inside the icalendar file), together with a message describing the
 * problem.
 */
static void
ical_log (FILE *log, ical_types_e type, unsigned lineno, char *msg)
{
  const char *typestr[ICAL_TYPES] = {"VEVENT", "VTODO"};

  RETURN_IF (type < 0 || type >= ICAL_TYPES, _("unknown ical type"));
  if (log)
    (void)fprintf (log, "%s [%d]: %s\n", typestr[type], lineno, msg);
}

static void
ical_store_todo (int priority, char *mesg, char *note)
{
  todo_add (mesg, priority, note);
  mem_free (mesg);
  erase_note (&note, ERASE_FORCE_KEEP_NOTE);
}

static void
ical_store_event (char *mesg, char *note, long day, long end, ical_rpt_t *rpt,
                  llist_t *exc)
{
  const int EVENTID = 1;

  if (rpt)
    {
      recur_event_new (mesg, note, day, EVENTID, rpt->type, rpt->freq,
                       rpt->until, exc);
      mem_free (rpt);
    }
  else if (end && end != day)
    {
      /* Here we have an event that spans over several days. */
      rpt = mem_malloc (sizeof (ical_rpt_t));
      rpt->type = RECUR_DAILY;
      rpt->freq = 1;
      rpt->count = 0;
      rpt->until = end;
      recur_event_new (mesg, note, day, EVENTID, rpt->type, rpt->freq,
                       rpt->until, exc);
      mem_free (rpt);
    }
  else
    {
      event_new (mesg, note, day, EVENTID);
    }
  mem_free (mesg);
  erase_note (&note, ERASE_FORCE_KEEP_NOTE);
}

static void
ical_store_apoint (char *mesg, char *note, long start, long dur,
                   ical_rpt_t *rpt, llist_t *exc, int has_alarm)
{
  char state = 0L;

  if (has_alarm)
    state |= APOINT_NOTIFY;
  if (rpt)
    {
      recur_apoint_new (mesg, note, start, dur, state, rpt->type, rpt->freq,
                        rpt->until, exc);
      mem_free (rpt);
    }
  else
    {
      apoint_new (mesg, note, start, dur, state);
    }
  mem_free (mesg);
  erase_note (&note, ERASE_FORCE_KEEP_NOTE);
}

/*
 * Returns an allocated string representing the string given in argument once
 * unformatted.
 *
 * Note:
 * Even if the RFC2445 recommends not to have more than 75 octets on one line of
 * text, I prefer not to restrict the parsing to this size, thus I use a buffer
 * of size BUFSIZ.
 *
 * Extract from RFC2445:
 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
 * break.
 */
static char *
ical_unformat_line (char *line)
{
  char *p, uline[BUFSIZ];
  int len;

  if (strlen (line) >= BUFSIZ)
    return NULL;

  bzero (uline, BUFSIZ);
  for (len = 0, p = line; *p; p++)
    {
      switch (*p)
        {
        case '\\':
          switch (*(p + 1))
            {
            case 'n':
              uline[len++] = '\n';
              p++;
              break;
            case 't':
              uline[len++] = '\t';
              p++;
              break;
            case ';':
            case ':':
            case ',':
              uline[len++] = *(p + 1);
              p++;
              break;
            default:
              uline[len++] = *p;
              break;
            }
          break;
        default:
          uline[len++] = *p;
          break;
        }
    }

  return mem_strdup (uline);
}

static void
ical_readline_init (FILE *fdi, char *buf, char *lstore, unsigned *ln)
{
  char *eol;

  *buf = *lstore = '\0';
  fgets (lstore, BUFSIZ, fdi);
  if ((eol = strchr(lstore, '\n')) != NULL)
    *eol = '\0';
  (*ln)++;
}

static int
ical_readline (FILE *fdi, char *buf, char *lstore, unsigned *ln)
{
  char *eol;

  strncpy (buf, lstore, BUFSIZ);
  (*ln)++;

  while (fgets (lstore, BUFSIZ, fdi) != NULL)
    {
      if ((eol = strchr(lstore, '\n')) != NULL)
        *eol = '\0';
      if (*lstore != SPACE && *lstore != TAB)
        break;
      strncat (buf, lstore + 1, BUFSIZ);
      buf[BUFSIZ - 1] = '\0';
      (*ln)++;
    }

  if (feof (fdi))
    {
      *lstore = '\0';
      if (*buf == '\0')
        return 0;
    }

  return 1;
}

static float
ical_chk_header (FILE *fd, char *buf, char *lstore, unsigned *lineno)
{
  const int HEADER_MALFORMED = -1;
  const struct string icalheader = STRING_BUILD ("BEGIN:VCALENDAR");
  float version;

  if (!ical_readline (fd, buf, lstore, lineno))
    return HEADER_MALFORMED;

  str_toupper (buf);
  if (strncmp (buf, icalheader.str, icalheader.len) != 0)
    return HEADER_MALFORMED;

  while (!sscanf (buf, "VERSION:%f", &version))
    {
      if (!ical_readline (fd, buf, lstore, lineno))
        return HEADER_MALFORMED;
    }
  return version;
}

/*
 * iCalendar date-time format is based on the ISO 8601 complete
 * representation. It should be something like : DATE 'T' TIME
 * where DATE is 'YYYYMMDD' and TIME is 'HHMMSS'.
 * The time and 'T' separator are optional (in the case of an day-long event).
 *
 * Optionnaly, if the type pointer is given, specify if it is an event
 * (no time is given, meaning it is an all-day event), or an appointment
 * (time is given).
 *
 * The timezone is not yet handled by calcurse.
 */
static long
ical_datetime2long (char *datestr, ical_vevent_e *type)
{
  const int NOTFOUND = 0, FORMAT_DATE = 3, FORMAT_DATETIME = 5;
  struct date date;
  unsigned hour, min;
  long datelong;
  int format;

  format = sscanf (datestr, "%04u%02u%02uT%02u%02u",
                   &date.yyyy, &date.mm, &date.dd, &hour, &min);
  if (format == FORMAT_DATE)
    {
      if (type)
        *type = EVENT;
      datelong = date2sec (date, 0, 0);
    }
  else if (format == FORMAT_DATETIME)
    {
      if (type)
        *type = APPOINTMENT;
      datelong = date2sec (date, hour, min);
    }
  else
    {
      datelong = NOTFOUND;
    }
  return datelong;
}

static long
ical_durtime2long (char *timestr)
{
  long timelong;
  char *p;

  if ((p = strchr (timestr, 'T')) == NULL)
    timelong = 0;
  else
    {
      int nbmatch;
      struct {
        unsigned hour, min, sec;
      } time;

      p++;
      bzero (&time, sizeof time);
      nbmatch = sscanf (p, "%uH%uM%uS", &time.hour, &time.min, &time.sec);
      if (nbmatch < 1 || nbmatch > 3)
        timelong = 0;
      else
        timelong = time.hour * HOURINSEC + time.min * MININSEC + time.sec;
    }
  return timelong;
}

/*
 * Extract from RFC2445:
 *
 * Value Name: DURATION
 *
 * Purpose: This value type is used to identify properties that contain
 * duration of time.
 *
 * Formal Definition: The value type is defined by the following
 * notation:
 *
 * dur-value  = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
 * dur-date   = dur-day [dur-time]
 * dur-time   = "T" (dur-hour / dur-minute / dur-second)
 * dur-week   = 1*DIGIT "W"
 * dur-hour   = 1*DIGIT "H" [dur-minute]
 * dur-minute = 1*DIGIT "M" [dur-second]
 * dur-second = 1*DIGIT "S"
 * dur-day    = 1*DIGIT "D"
 *
 * Example: A duration of 15 days, 5 hours and 20 seconds would be:
 * P15DT5H0M20S
 * A duration of 7 weeks would be:
 * P7W
 */
static long
ical_dur2long (char *durstr)
{
  const int NOTFOUND = -1;
  long durlong;
  char *p;
  struct {
    unsigned week, day;
  } date;

  bzero (&date, sizeof date);
  if ((p = strchr (durstr, 'P')) == NULL)
    durlong = NOTFOUND;
  else
    {
      p++;
      if (*p == '-')
        return NOTFOUND;
      else if (*p == '+')
        p++;

      if (*p == 'T')                                      /* dur-time */
        durlong = ical_durtime2long (p);
      else if (strchr (p, 'W'))                           /* dur-week */
        {
          if (sscanf (p, "%u", &date.week) == 1)
            durlong = date.week * WEEKINDAYS * DAYINSEC;
          else
            durlong = NOTFOUND;
        }
      else
        {
          if (strchr (p, 'D'))                            /* dur-date */
            {
              if (sscanf (p, "%uD", &date.day) == 1)
                {
                  durlong = date.day * DAYINSEC;
                  durlong += ical_durtime2long (p);
                }
              else
                durlong = NOTFOUND;
            }
          else
            durlong