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