1 /*
2  *  rolo - contact management software
3  *  Copyright (C) 2003  Andrew Hsu
4  *
5  *  This program is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU General Public License as
7  *  published by the Free Software Foundation; either version 2 of
8  *  the License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18  *  02111-1307 USA
19  *
20  *  $Id: main.c,v 1.15 2003/05/20 01:08:52 ahsu Rel $
21  */
22 
23 #include <vc.h>
24 #include "add.h"
25 #include "view.h"
26 #include "delete.h"
27 #include "edit.h"
28 #include "index.h"
29 #include "help.h"
30 #include <errno.h>
31 #include <limits.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <menu.h>
38 #include <assert.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 
42 #if HAVE_CONFIG_H
43 #include "config.h"
44 #else
45 #define PACKAGE_STRING "rolo"
46 #endif
47 
48 #define DEFAULT_HOME_ROLO_DIR ".rolo"
49 #define DEFAULT_FILENAME "contacts.vcf"
50 
51 /*** GLOBALS ***/
52 
53 enum win_states
54 { WINDOW_INDEX, WINDOW_VIEW, WINDOW_RAW_VIEW, WINDOW_EDIT, WINDOW_DELETE,
55   WINDOW_ADD
56 };
57 char data_path[PATH_MAX];
58 
59 /*** PROTOTYPES ***/
60 
61 static void finish (int sig);
62 static void resize (int sig);
63 static void set_defaults ();
64 static void process_command_line_args (int argc, char *const *argv);
65 static void display_usage (const char *prog_name);
66 static void display_version ();
67 static void set_contacts_file ();
68 static void display_license ();
69 static char *get_env_editor ();
70 
71 /***************************************************************************
72     Gets the editor to invoke from the environment settings.  If
73     none of the environment settings are set, then the default value
74     of `vi' will be returned.  The user of this function must
75     remember to free the returned string.
76  */
77 
78 static char *
get_env_editor()79 get_env_editor ()
80 {
81   char *editor = NULL;
82 
83   editor = getenv ("ROLO_EDITOR");
84 
85   if (NULL == editor)
86     {
87       editor = getenv ("EDITOR");
88       if (NULL == editor)
89         {
90           editor = strdup ("vi");
91           return editor;
92         }
93     }
94 
95   return strdup (editor);
96 }
97 
98 /***************************************************************************
99     This is called upon when the window is resized.
100  */
101 
102 static void
resize(int sig)103 resize (int sig)
104 {
105   finish_index ();
106   endwin ();
107   refresh ();
108   initscr ();
109 
110   keypad (stdscr, TRUE);        /* enable keypad for use of arrow keys */
111   nonl ();                      /* tell curses not to do NL->CR/NL on output */
112   cbreak ();                    /* take input chars immediately */
113   noecho ();
114 
115   init_index (data_path);
116   display_index ();
117   refresh ();
118 }
119 
120 /***************************************************************************
121     This is called upon when the program is asked to finish.
122  */
123 
124 static void
finish(int sig)125 finish (int sig)
126 {
127   finish_index ();
128   endwin ();
129   exit (0);
130 }
131 
132 /***************************************************************************
133     Sets the default program startup values.
134  */
135 
136 static void
set_defaults()137 set_defaults ()
138 {
139   char default_datafile[PATH_MAX];
140   char *home = NULL;
141   char *editor = NULL;
142 
143   home = getenv ("HOME");
144   if (NULL != home)
145     {
146       int result = 1;
147       struct stat sb;
148 
149       strcpy (default_datafile, home);
150       result = stat (default_datafile, &sb);
151       if (-1 == result)
152         {
153           fprintf (stderr, "home directory unavailable\n");
154           exit (1);
155         }
156 
157       strncat (default_datafile, "/", 1);
158       strncat (default_datafile, DEFAULT_HOME_ROLO_DIR,
159                strlen (DEFAULT_HOME_ROLO_DIR));
160       result = stat (default_datafile, &sb);
161       if (-1 == result)
162         {
163           if (ENOENT == errno)
164             {
165               mkdir (default_datafile, S_IRWXU);
166             }
167           else
168             {
169               exit (1);
170             }
171         }
172 
173       strncat (default_datafile, "/", 1);
174       strncat (default_datafile, DEFAULT_FILENAME, strlen (DEFAULT_FILENAME));
175       result = stat (default_datafile, &sb);
176       if (-1 == result)
177         {
178           if (ENOENT == errno)
179             {
180               FILE *fp;
181 
182               fp = fopen (default_datafile, "w");
183               fclose (fp);
184             }
185           else
186             {
187               exit (1);
188             }
189         }
190     }
191   else
192     {
193       fprintf (stderr, "unable to deterime home directory");
194       exit (1);
195     }
196 
197   strcpy (data_path, default_datafile);
198 
199   editor = get_env_editor ();
200 
201   set_edit_editor (editor);
202   set_add_editor (editor);
203 }
204 
205 /***************************************************************************
206     Ouputs how to use the program.
207  */
208 
209 static void
display_usage(const char * prog_name)210 display_usage (const char *prog_name)
211 {
212   printf ("usage: %s [-r] [-f <file>]\n", prog_name);
213   printf ("       %s -v\n", prog_name);
214   printf ("       %s -V\n", prog_name);
215   printf ("       %s -h\n", prog_name);
216   printf ("options:\n");
217   printf ("  -r            open the contact file as read-only\n");
218   printf ("  -f <file>     specify a contact file to use\n");
219   printf ("  -v            display version\n");
220   printf ("  -V            display copyright and license\n");
221   printf ("  -h            this help message\n");
222 }
223 
224 /***************************************************************************
225     Outputs a one-line version statement.
226  */
227 
228 static void
display_version()229 display_version ()
230 {
231   printf ("%s\n", PACKAGE_STRING);
232 }
233 
234 /***************************************************************************
235     Outputs the software license.
236  */
237 
238 static void
display_license()239 display_license ()
240 {
241   printf ("rolo - contact management software\n");
242   printf ("Copyright (C) 2003  Andrew Hsu\n");
243   printf ("\n");
244   printf ("This program is free software;");
245   printf (" you can redistribute it and/or modify\n");
246   printf ("it under the terms of the");
247   printf (" GNU General Public License as published by\n");
248   printf ("the Free Software Foundation;");
249   printf (" either version 2 of the License, or\n");
250   printf ("(at your option) any later version.\n");
251   printf ("\n");
252   printf ("This program is distributed");
253   printf (" in the hope that it will be useful,\n");
254   printf ("but WITHOUT ANY WARRANTY;");
255   printf (" without even the implied warranty of\n");
256   printf ("MERCHANTABILITY or FITNESS FOR A PARTICULAR");
257   printf (" PURPOSE.  See the\n");
258   printf ("GNU General Public License for more details.\n");
259   printf ("\n");
260   printf ("You should have received a copy of");
261   printf (" the GNU General Public License\n");
262   printf ("along with this program;");
263   printf (" if not, write to the Free Software\n");
264   printf ("Foundation, Inc., 59 Temple Place, Suite 330,");
265   printf (" Boston, MA  02111-1307  USA\n");
266 }
267 
268 /***************************************************************************
269     Helper function for setting the contact file.
270  */
271 
272 static void
set_contacts_file()273 set_contacts_file ()
274 {
275   strncpy (data_path, optarg, PATH_MAX);
276   data_path[PATH_MAX - 1] = '\0';
277 }
278 
279 /***************************************************************************
280     Parses the command-line arguments.
281  */
282 
283 static void
process_command_line_args(int argc,char * const * argv)284 process_command_line_args (int argc, char *const *argv)
285 {
286   int ch = -1;
287 
288   while (-1 != (ch = getopt (argc, argv, "rf:vVh")))
289     {
290       switch (ch)
291         {
292         case 'r':
293           /* FIXME: implement read-only option */
294           break;
295         case 'f':
296           set_contacts_file ();
297           break;
298         case 'v':
299           display_version ();
300           exit (0);
301           break;
302         case 'V':
303           display_license ();
304           exit (0);
305           break;
306         case 'h':
307         case '?':
308         default:
309           display_usage (argv[0]);
310           exit (0);
311         }
312     }
313 }
314 
315 /***************************************************************************
316     The main function.
317  */
318 
319 int
main(int argc,char * argv[])320 main (int argc, char *argv[])
321 {
322   vc_component *v = NULL;
323   fpos_t *fpos = NULL;
324   long pos = 0;
325   FILE *fp = NULL;
326   ITEM *it = NULL;
327   int entry_number = 0;
328 
329   bool done = FALSE;
330   int win_state = WINDOW_INDEX;
331   int command = 0;
332 
333   set_defaults ();
334   process_command_line_args (argc, argv);
335   /*
336    * process_environment_variables();
337    * process_configuration_file();
338    */
339 
340   signal (SIGINT, finish);      /* catch interrupt for exiting */
341   signal (SIGWINCH, resize);    /* catch interrupt for resizing */
342   initscr ();
343 
344   keypad (stdscr, TRUE);        /* enable keypad for use of arrow keys */
345   nonl ();                      /* tell curses not to do NL->CR/NL on output */
346   cbreak ();                    /* take input chars immediately */
347   noecho ();
348 
349   init_index (data_path);
350   set_index_help_fcn (show_index_help);
351   init_view ();
352   set_view_help_fcn (show_view_help);
353   init_edit ();
354   set_edit_help_fcn (show_edit_help);
355   init_help ();
356 
357   while (!done)
358     {
359       switch (win_state)
360         {
361         case WINDOW_INDEX:
362 
363       /*-------------------
364          display the index
365         -------------------*/
366 
367           display_index ();
368           command = process_index_commands ();
369 
370           switch (command)
371             {
372             case INDEX_COMMAND_VIEW:
373               win_state = WINDOW_VIEW;
374               break;
375             case INDEX_COMMAND_RAW_VIEW:
376               win_state = WINDOW_RAW_VIEW;
377               break;
378             case INDEX_COMMAND_EDIT:
379               win_state = WINDOW_EDIT;
380               break;
381             case INDEX_COMMAND_ADD:
382               win_state = WINDOW_ADD;
383               break;
384             case INDEX_COMMAND_DELETE:
385               win_state = WINDOW_DELETE;
386               break;
387             case INDEX_COMMAND_QUIT:
388               done = TRUE;
389               break;
390             default:
391               break;
392             }
393 
394           break;
395 
396         case WINDOW_RAW_VIEW:
397 
398       /*-------------------------------------------------
399          view the currently selected item with the pager
400         -------------------------------------------------*/
401 
402           it = get_current_item ();
403 
404           /* only display if there is an item that is selected */
405           if (NULL == it)
406             {
407               v = NULL;
408             }
409           else
410             {
411               fpos = (fpos_t *) item_userptr (it);
412 
413               fp = fopen (data_path, "r");
414               fsetpos (fp, fpos);
415               v = parse_vcard_file (fp);
416               fclose (fp);
417             }
418 
419           if (v != NULL)
420             {
421               raw_view (v);
422               vc_delete_deep (v);
423               v = NULL;
424             }
425 
426           win_state = WINDOW_INDEX;
427 
428           break;
429 
430         case WINDOW_VIEW:
431 
432       /*----------------------------------
433          view the currently selected item
434         ----------------------------------*/
435 
436           it = get_current_item ();
437 
438           /* only display if there is an item that is selected */
439           if (NULL == it)
440             {
441               v = NULL;
442             }
443           else
444             {
445               fpos = (fpos_t *) item_userptr (it);
446 
447               fp = fopen (data_path, "r");
448               fsetpos (fp, fpos);
449               v = parse_vcard_file (fp);
450               fclose (fp);
451             }
452 
453           if (v != NULL)
454             {
455               entry_number = get_entry_number (it);
456               view_vcard (entry_number, v);
457               command = process_view_commands ();
458 
459               switch (command)
460                 {
461                 case VIEW_COMMAND_EDIT:
462                   win_state = WINDOW_EDIT;
463                   break;
464                 case VIEW_COMMAND_INDEX:
465                   win_state = WINDOW_INDEX;
466                   break;
467                 case VIEW_COMMAND_PREVIOUS:
468                   select_previous_item ();
469                   win_state = WINDOW_VIEW;
470                   break;
471                 case VIEW_COMMAND_NEXT:
472                   select_next_item ();
473                   win_state = WINDOW_VIEW;
474                   break;
475                 default:
476                   break;
477                 }
478             }
479           else
480             {
481               win_state = WINDOW_INDEX;
482             }
483 
484           vc_delete_deep (v);
485           v = NULL;
486 
487           break;
488 
489         case WINDOW_EDIT:
490 
491       /*--------------
492          edit a vcard
493         --------------*/
494 
495           it = get_current_item ();
496 
497           /* only display if there is an item that is selected */
498           if (NULL != it)
499             {
500               fpos = (fpos_t *) item_userptr (it);
501 
502               fp = fopen (data_path, "r");
503               fsetpos (fp, fpos);
504               pos = ftell (fp);
505               fclose (fp);
506               fp = NULL;
507 
508               if (EDIT_SUCCESSFUL == edit_entry (data_path, pos))
509                 {
510                   refresh_index ();
511                 }
512             }
513 
514           win_state = WINDOW_INDEX;
515           break;
516 
517         case WINDOW_ADD:
518           if (ADD_SUCCESSFUL == add_entry (data_path))
519             {
520               refresh_index ();
521             }
522 
523           win_state = WINDOW_INDEX;
524           break;
525         case WINDOW_DELETE:
526 
527           it = get_current_item ();
528 
529           /* only delete if there is an item that is selected */
530           if (NULL != it)
531             {
532               fpos = (fpos_t *) item_userptr (it);
533 
534               fp = fopen (data_path, "r");
535               fsetpos (fp, fpos);
536               pos = ftell (fp);
537               fclose (fp);
538               fp = NULL;
539 
540               if (DELETE_SUCCESSFUL == delete_entry (data_path, pos))
541                 {
542                   refresh_index ();
543                 }
544             }
545 
546           win_state = WINDOW_INDEX;
547           break;
548         default:
549           break;
550         }
551     }
552 
553   finish (0);
554   exit (EXIT_SUCCESS);
555   return (0);
556 }
557