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