]> git.deb.at Git - pkg/abook.git/blob - abook.c
Attempt to improve --datafile behavior
[pkg/abook.git] / abook.c
1 /*
2  * $Id$
3  *
4  * by JH <jheinonen@users.sourceforge.net>
5  *
6  * Copyright (C) Jaakko Heinonen
7  */
8
9 #include <string.h>
10 #include <unistd.h>
11 #include <stdlib.h>
12 #include <sys/stat.h>
13 #include <signal.h>
14 #include <fcntl.h>
15 #ifdef HAVE_CONFIG_H
16 #       include "config.h"
17 #endif
18 #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
19 #       include <locale.h>
20 #endif
21 #include "abook.h"
22 #include "ui.h"
23 #include "database.h"
24 #include "list.h"
25 #include "filter.h"
26 #include "edit.h"
27 #include "misc.h"
28 #include "options.h"
29 #include "getname.h"
30 #include "getopt.h"
31 #include <assert.h>
32
33 static void             init_abook();
34 static void             quit_abook_sig(int i);
35 static void             set_filenames();
36 static void             parse_command_line(int argc, char **argv);
37 static void             show_usage();
38 static void             mutt_query(char *str);
39 static void             init_mutt_query();
40 static void             convert(char *srcformat, char *srcfile,
41                                 char *dstformat, char *dstfile);
42 static void             add_email(int);
43
44 char *datafile = NULL;
45 char *rcfile = NULL;
46
47 int alternative_datafile = FALSE;
48 int alternative_rcfile = FALSE;
49
50 static int
51 datafile_writeable()
52 {
53         FILE *f;
54
55         assert(datafile != NULL);
56
57         if( (f = fopen(datafile, "a")) == NULL)
58                 return FALSE;
59
60         fclose(f);
61
62         return TRUE;
63 }
64
65 static void
66 init_abook()
67 {
68         set_filenames();
69         init_options();
70
71         signal(SIGKILL, quit_abook_sig);
72         signal(SIGTERM, quit_abook_sig);
73         
74         if( init_ui() )
75                 exit(1);
76         
77         umask(DEFAULT_UMASK);
78
79         if(!datafile_writeable()) {
80                 char *s = mkstr("File %s is not writeable", datafile);
81                 refresh_screen();
82                 statusline_msg(s);
83                 free(s);
84                 if(load_database(datafile) || !statusline_ask_boolean(
85                                         "If you continue all changes will "
86                                 "be lost. Do you want to continue?", FALSE)) {
87                         close_config();
88                         close_database();
89                         close_ui();
90                         exit(1);
91                 }
92         } else
93                 load_database(datafile);
94
95         refresh_screen();
96 }
97
98 void
99 quit_abook()
100 {
101         if( options_get_int("autosave") )
102                 save_database();
103         else if( statusline_ask_boolean("Save database", TRUE) )
104                 save_database();
105
106         close_config();
107         close_database();
108
109         close_ui();
110         
111         exit(0);
112 }
113
114 static void
115 quit_abook_sig(int i)
116 {
117         quit_abook();
118 }
119
120 int
121 main(int argc, char **argv)
122 {
123 #if defined(HAVE_SETLOCALE) && defined(HAVE_LOCALE_H)
124         setlocale(LC_ALL, "" );
125 #endif
126                 
127         parse_command_line(argc, argv);
128         
129         init_abook();
130
131         get_commands(); 
132         
133         quit_abook();
134
135         return 0;
136 }
137
138 static void
139 free_filenames()
140 {
141         my_free(rcfile);
142         my_free(datafile);
143 }
144
145
146 static void
147 set_filenames()
148 {
149         struct stat s;
150
151         if( (stat(getenv("HOME"), &s)) == -1 || ! S_ISDIR(s.st_mode) ) {
152                 fprintf(stderr,"%s is not a valid HOME directory\n", getenv("HOME") );
153                 exit(1);
154         }
155
156         if(!datafile)
157                 datafile = strconcat(getenv("HOME"), "/" DATAFILE, NULL);
158
159         if(!rcfile)
160                 rcfile = strconcat(getenv("HOME"), "/" RCFILE, NULL);
161
162         atexit(free_filenames);
163 }
164
165 /*
166  * CLI
167  */
168
169 enum {
170         MODE_CONT,
171         MODE_ADD_EMAIL,
172         MODE_ADD_EMAIL_QUIET,
173         MODE_QUERY,
174         MODE_CONVERT
175 };
176
177 static void
178 change_mode(int *current, int mode)
179 {
180         if(*current != MODE_CONT) {
181                 fprintf(stderr, "Cannot combine options --mutt-query, "
182                                 "--convert, "
183                                 "--add-email or --add-email-quiet\n");
184                 exit(1);
185         }
186
187         *current = mode;
188 }
189
190 void
191 set_filename(char **var, char *path)
192 {
193         char *cwd;
194         
195         assert(var != NULL);
196         assert(*var == NULL); /* or else we probably leak memory */
197         assert(path != NULL);
198         
199         if(*path == '/') {
200                 *var = strdup(path);
201                 return;
202         }
203
204         cwd = my_getcwd();
205
206         *var = strconcat(cwd, path, NULL);
207
208         free(cwd);
209 }
210
211 #define set_convert_var(X) do { if(mode != MODE_CONVERT) {\
212         fprintf(stderr, "please use option --%s after --convert option\n",\
213                         long_options[option_index].name);\
214                 exit(1);\
215         } else\
216                 X = optarg;\
217         } while(0)
218
219 static void
220 parse_command_line(int argc, char **argv)
221 {
222         int mode = MODE_CONT;
223         char *query_string = NULL;
224         char *informat = "abook",
225                 *outformat = "text",
226                 *infile = "-",
227                 *outfile = "-";
228         int c;
229
230         for(;;) {
231                 int option_index = 0;
232                 enum {
233                         OPT_ADD_EMAIL,
234                         OPT_ADD_EMAIL_QUIET,
235                         OPT_MUTT_QUERY,
236                         OPT_CONVERT,
237                         OPT_INFORMAT,
238                         OPT_OUTFORMAT,
239                         OPT_INFILE,
240                         OPT_OUTFILE,
241                         OPT_FORMATS
242                 };
243                 static struct option long_options[] = {
244                         { "help", 0, 0, 'h' },
245                         { "add-email", 0, 0, OPT_ADD_EMAIL },
246                         { "add-email-quiet", 0, 0, OPT_ADD_EMAIL_QUIET },
247                         { "datafile", 1, 0, 'f' },
248                         { "mutt-query", 1, 0, OPT_MUTT_QUERY },
249                         { "config", 1, 0, 'C' },
250                         { "convert", 0, 0, OPT_CONVERT },
251                         { "informat", 1, 0, OPT_INFORMAT },
252                         { "outformat", 1, 0, OPT_OUTFORMAT },
253                         { "infile", 1, 0, OPT_INFILE },
254                         { "outfile", 1, 0, OPT_OUTFILE },
255                         { "formats", 0, 0, OPT_FORMATS },
256                         { 0, 0, 0, 0 }
257                 };
258
259                 c = getopt_long(argc, argv, "hC:",
260                                 long_options, &option_index);
261
262                 if(c == -1)
263                         break;
264
265                 switch(c) {
266                         case 'h':
267                                 show_usage();
268                                 exit(1);
269                         case OPT_ADD_EMAIL:
270                                 change_mode(&mode, MODE_ADD_EMAIL);
271                                 break;
272                         case OPT_ADD_EMAIL_QUIET:
273                                 change_mode(&mode, MODE_ADD_EMAIL_QUIET);
274                                 break;
275                         case 'f':
276                                 set_filename(&datafile, optarg);
277                                 alternative_datafile = TRUE;
278                                 break;
279                         case OPT_MUTT_QUERY:
280                                 query_string = optarg;
281                                 change_mode(&mode, MODE_QUERY);
282                                 break;
283                         case 'C':
284                                 set_filename(&rcfile, optarg);
285                                 alternative_rcfile = TRUE;
286                                 break;
287                         case OPT_CONVERT:
288                                 change_mode(&mode, MODE_CONVERT);
289                                 break;
290                         case OPT_INFORMAT:
291                                 set_convert_var(informat);
292                                 break;
293                         case OPT_OUTFORMAT:
294                                 set_convert_var(outformat);
295                                 break;
296                         case OPT_INFILE:
297                                 set_convert_var(infile);
298                                 break;
299                         case OPT_OUTFILE:
300                                 set_convert_var(outfile);
301                                 break;
302                         case OPT_FORMATS:
303                                 print_filters();
304                                 exit(0);
305                         default:
306                                 exit(1);
307                 }
308         }
309
310         if (optind < argc) {
311                 fprintf(stderr, "%s: unrecognized arguments on command line\n",
312                                 argv[0]);
313                 exit(1);
314         }
315
316         switch(mode) {
317                 case MODE_ADD_EMAIL:
318                         add_email(0);
319                 case MODE_ADD_EMAIL_QUIET:
320                         add_email(1);
321                 case MODE_QUERY:
322                         mutt_query(query_string);
323                 case MODE_CONVERT:
324                         convert(informat, infile, outformat, outfile);
325         }
326 }
327
328
329 static void
330 show_usage()
331 {
332         puts    (PACKAGE " v " VERSION "\n");
333         puts    ("     -h       --help                          show usage");
334         puts    ("     -C       --config        <file>          use an alternative configuration file");   
335         puts    ("      --datafile      <file>          use an alternative addressbook file");
336         puts    ("      --mutt-query    <string>        make a query for mutt");
337         puts    ("      --add-email                     "
338                         "read an e-mail message from stdin and\n"
339                 "                                       "
340                 "add the sender to the addressbook");
341         puts    ("      --add-email-quiet               "
342                 "same as --add-email but doesn't\n"
343                 "                                       confirm adding");
344         putchar('\n');
345         puts    ("      --convert                       convert address book files");
346         puts    ("      options to use with --convert:");
347         puts    ("      --informat      <format>        format for input file");
348         puts    ("                                      (default: abook)");
349         puts    ("      --infile        <file>          source file");
350         puts    ("                                      (default: stdin)");
351         puts    ("      --outformat     <format>        format for output file");
352         puts    ("                                      (default: text)");
353         puts    ("      --outfile       <file>          destination file");
354         puts    ("                                      (default: stdout)");
355         puts    ("      --formats                       list available formats");
356 }
357
358 /*
359  * end of CLI
360  */
361
362 extern list_item *database;
363
364 static void
365 quit_mutt_query(int status)
366 {
367         close_database();
368         close_config();
369
370         exit(status);
371 }
372
373 static void
374 muttq_print_item(FILE *file, int item)
375 {
376         char emails[MAX_EMAILS][MAX_EMAIL_LEN];
377         int i;
378
379         split_emailstr(item, emails);
380         
381         for(i = 0; i < (options_get_int("mutt_return_all_emails") ?
382                         MAX_EMAILS : 1) ; i++)
383                 if( *emails[i] )
384                         fprintf(file, "%s\t%s\t%s\n", emails[i],
385                                 database[item][NAME],
386                                 database[item][NOTES] == NULL ? " " :
387                                         database[item][NOTES]
388                                 );
389 }
390
391 static void
392 mutt_query(char *str)
393 {
394         init_mutt_query();
395
396         if( str == NULL || !strcasecmp(str, "all") ) {
397                 struct db_enumerator e = init_db_enumerator(ENUM_ALL);
398                 printf("All items\n");
399                 db_enumerate_items(e)
400                         muttq_print_item(stdout, e.item);
401         } else {
402                 int search_fields[] = {NAME, EMAIL, NICK, -1};
403                 int i;
404                 if( (i = find_item(str, 0, search_fields)) < 0 ) {
405                         printf("Not found\n");
406                         quit_mutt_query(1);
407                 }
408                 putchar('\n');
409                 while(i >= 0) {
410                         muttq_print_item(stdout, i);
411                         i = find_item(str, i+1, search_fields);
412                 }
413         }
414
415         quit_mutt_query(0);
416 }
417
418 static void
419 init_mutt_query()
420 {
421         set_filenames();
422         init_options();
423         
424         if( load_database(datafile) ) {
425                 printf("Cannot open database\n");
426                 quit_mutt_query(1);
427                 exit(1);
428         }
429 }
430
431
432 static char *
433 make_mailstr(int item)
434 {
435         char email[MAX_EMAIL_LEN];
436         char *ret;
437         char *name = mkstr("\"%s\"", database[item][NAME]);
438
439         get_first_email(email, item);
440
441         ret = *database[item][EMAIL] ?
442                 mkstr("%s <%s>", name, email) :
443                 strdup(name);
444
445         free(name);
446         
447         return ret;
448 }
449
450 void
451 print_stderr(int item)
452 {
453         fprintf (stderr, "%c", '\n');
454
455         if( is_valid_item(item) )
456                 muttq_print_item(stderr, item);
457         else {
458                 struct db_enumerator e = init_db_enumerator(ENUM_SELECTED);
459                 db_enumerate_items(e) {
460                         muttq_print_item(stderr, e.item);
461                 }
462         }
463
464 }
465
466 void
467 launch_mutt(int item)
468 {
469         char *cmd = NULL, *mailstr = NULL;
470         char *mutt_command = options_get_str("mutt_command");
471
472         if(mutt_command == NULL || !*mutt_command)
473                 return;
474
475         if( is_valid_item(item) )
476                 mailstr = make_mailstr(item);
477         else {
478                 struct db_enumerator e = init_db_enumerator(ENUM_SELECTED);
479                 char *tmp = NULL;
480                 db_enumerate_items(e) {
481                         tmp = mailstr;
482                         mailstr = tmp ?
483                                 strconcat(tmp, ",", make_mailstr(e.item), NULL):
484                                 strconcat(make_mailstr(e.item), NULL);
485                         free(tmp);
486                 }
487         }
488
489         cmd = strconcat(mutt_command, " \'", mailstr,
490                                 "\'", NULL);
491         free(mailstr);
492 #ifdef DEBUG
493         fprintf(stderr, "cmd: %s\n", cmd);
494 #endif
495         system(cmd);    
496         free(cmd);
497         
498         /*
499          * we need to make sure that curses settings are correct
500          */
501         ui_init_curses();
502 }
503
504 void
505 launch_wwwbrowser(int item)
506 {
507         char *cmd = NULL;
508
509         if( !is_valid_item(item) )
510                 return;
511
512         if( database[item][URL] )
513                 cmd = mkstr("%s '%s'",
514                                 options_get_str("www_command"),
515                                 safe_str(database[item][URL]));
516         else
517                 return;
518
519         if ( cmd )
520                 system(cmd);
521
522         free(cmd);
523
524         /*
525          * we need to make sure that curses settings are correct
526          */
527         ui_init_curses();
528 }
529
530 void *
531 abook_malloc(size_t size)
532 {
533         void *ptr;
534
535         if ( (ptr = malloc(size)) == NULL ) {
536                 if( is_ui_initialized() )
537                         quit_abook();
538                 perror("malloc() failed");
539                 exit(1);
540         }
541
542         return ptr;
543 }
544
545 void *
546 abook_realloc(void *ptr, size_t size)
547 {
548         ptr = realloc(ptr, size);
549
550         if( size == 0 )
551                 return NULL;
552
553         if( ptr == NULL ) {
554                 if( is_ui_initialized() )
555                         quit_abook();
556                 perror("realloc() failed");
557                 exit(1);
558         }
559
560         return ptr;
561 }
562
563 FILE *
564 abook_fopen (const char *path, const char *mode)
565 {       
566         struct stat s;
567         
568         if( ! strchr(mode, 'r') )
569                 return fopen(path, mode);
570         
571         if ( (stat(path, &s)) == -1 )
572                 return NULL;
573         
574         return S_ISREG(s.st_mode) ? fopen(path, mode) : NULL;
575 }
576
577
578
579 static void
580 convert(char *srcformat, char *srcfile, char *dstformat, char *dstfile)
581 {
582         int ret=0;
583
584         if( !srcformat || !srcfile || !dstformat || !dstfile ) {
585                 fprintf(stderr, "too few argumets to make conversion\n");
586                 fprintf(stderr, "try --help\n");
587         }
588
589 #ifndef DEBUG
590         if( !strcasecmp(srcformat, dstformat) ) {
591                 printf( "input and output formats are the same\n"
592                         "exiting...\n");
593                 exit(1);
594         }
595 #endif
596
597         set_filenames();
598         init_options();
599
600         switch( import_file(srcformat, srcfile) ) {
601                 case -1:
602                         fprintf(stderr,
603                                 "input format %s not supported\n", srcformat);
604                         ret = 1;
605                         break;
606                 case 1:
607                         fprintf(stderr, "cannot read file %s\n", srcfile);
608                         ret = 1;
609                         break;
610         }
611
612         if(!ret)
613                 switch( export_file(dstformat, dstfile) ) {
614                         case -1:
615                                 fprintf(stderr,
616                                         "output format %s not supported\n",
617                                         dstformat);
618                                 ret = 1;
619                                 break;
620                         case 1:
621                                 fprintf(stderr,
622                                         "cannot write file %s\n", dstfile);
623                                 ret = 1;
624                                 break;
625                 }
626
627         close_database();
628         close_config();
629         exit(ret);
630 }
631
632 /*
633  * --add-email handling
634  */
635
636 static int add_email_count = 0;
637
638 static void
639 quit_add_email()
640 {
641         if(add_email_count > 0) {
642                 if(save_database() < 0) {
643                         fprintf(stderr, "cannot open %s\n", datafile);
644                         exit(1);
645                 }
646                 printf("%d item(s) added to %s\n", add_email_count, datafile);
647         } else {
648                 puts("Valid sender address not found");
649         }
650
651         exit(0);
652 }
653
654 static void
655 quit_add_email_sig(int signal)
656 {
657         quit_add_email();
658 }
659
660 static void
661 init_add_email()
662 {
663         set_filenames();
664         atexit(free_filenames);
665         init_options();
666         atexit(close_config);
667         
668         /*
669          * we don't actually care if loading fails or not
670          */
671         load_database(datafile);
672
673         atexit(close_database);
674
675         signal(SIGINT, quit_add_email_sig);
676 }
677
678 static int
679 add_email_add_item(int quiet, char *name, char *email)
680 {
681         list_item item;
682
683         if(!quiet) {
684                 FILE *in = fopen("/dev/tty", "r");
685                 char c;
686                 if(!in) {
687                         fprintf(stderr, "cannot open /dev/tty\n"
688                                 "you may want to use --add-email-quiet\n");
689                         exit(1);
690                 }
691                 printf("Add ``%s <%s>'' to %s ? (y/n)\n",
692                                 name,
693                                 email,
694                                 datafile
695                 );
696                 do {
697                         c = fgetc(in);
698                         if(c == 'n' || c == 'N') {
699                                 fclose(in);
700                                 return 0;
701                         }
702                 } while(c != 'y' && c != 'Y');
703                 fclose(in);
704         }
705
706         memset(item, 0, sizeof(item));
707         item[NAME] = strdup(name);
708         item[EMAIL] = strdup(email);
709         add_item2database(item);
710
711         return 1;
712 }
713
714 static void
715 add_email(int quiet)
716 {
717         char *line;
718         char *name = NULL, *email = NULL;
719         
720         init_add_email();
721
722         do {
723                 line = getaline(stdin);
724                 if(line && !strncasecmp("From:", line, 5) ) {
725                         getname(line, &name, &email);
726                         add_email_count += add_email_add_item(quiet,
727                                         name, email);
728                         my_free(name);
729                         my_free(email);
730                 }
731                 my_free(line);
732         } while( !feof(stdin) );
733
734         quit_add_email();
735 }
736
737 /*
738  * end of --add-email handling
739  */