4 * by JH <jheinonen@users.sourceforge.net>
6 * Copyright (C) Jaakko Heinonen
21 #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
39 static void init_abook();
40 static void quit_abook_sig(int i);
41 static void set_filenames();
42 static void parse_command_line(int argc, char **argv);
43 static void show_usage();
44 static void mutt_query(char *str);
45 static void init_mutt_query();
46 static void convert(char *srcformat, char *srcfile,
47 char *dstformat, char *dstfile);
48 static void add_email(int);
50 char *datafile = NULL;
51 static char *rcfile = NULL;
54 char custom_format[FORMAT_STRING_LEN] = "{nick} ({name}): {mobile}";
55 char *parsed_custom_format = NULL;
56 enum field_types *custom_format_fields = 0;
57 struct abook_output_item_filter selected_item_filter;
59 bool alternative_datafile = FALSE;
60 bool alternative_rcfile = FALSE;
68 assert(datafile != NULL);
70 if( (f = fopen(datafile, "a")) == NULL)
79 check_abook_directory()
84 assert(!is_ui_initialized());
86 if(alternative_datafile)
89 dir = strconcat(getenv("HOME"), "/" DIR_IN_HOME, NULL);
92 if(stat(dir, &s) == -1) {
98 if(mkdir(dir, 0700) == -1) {
99 printf(_("Cannot create directory %s\n"), dir);
104 } else if(!S_ISDIR(s.st_mode)) {
105 printf(_("%s is not a directory\n"), dir);
114 xmalloc_error_handler(int err)
117 * We don't try to save addressbook here because we don't know
118 * if it's fully loaded to memory.
120 if(is_ui_initialized())
123 fprintf(stderr, _("Memory allocation failure: %s\n"), strerror(err));
131 check_abook_directory();
133 if(load_opts(rcfile) > 0) {
134 printf(_("Press enter to continue...\n"));
137 init_default_views();
139 signal(SIGTERM, quit_abook_sig);
146 umask(DEFAULT_UMASK);
148 if(!datafile_writeable()) {
149 char *s = strdup_printf(_("File %s is not writeable"), datafile);
153 if(load_database(datafile) || !statusline_ask_boolean(
154 _("If you continue all changes will "
155 "be lost. Do you want to continue?"), FALSE)) {
157 /*close_database();*/
162 load_database(datafile);
168 quit_abook(int save_db)
171 if(opt_get_bool(BOOL_AUTOSAVE))
173 else if(statusline_ask_boolean(_("Save database"), TRUE))
175 } else if(!statusline_ask_boolean(_("Quit without saving"), FALSE))
187 quit_abook_sig(int i)
189 quit_abook(QUIT_SAVE);
193 main(int argc, char **argv)
195 #if defined(HAVE_SETLOCALE) && defined(HAVE_LOCALE_H)
196 setlocale(LC_MESSAGES, "");
197 setlocale(LC_TIME, "");
198 setlocale(LC_CTYPE, "");
199 setlocale(LC_COLLATE, "");
201 bindtextdomain(PACKAGE, LOCALEDIR);
204 xmalloc_set_error_handler(xmalloc_error_handler);
206 prepare_database_internals();
208 parse_command_line(argc, argv);
214 quit_abook(QUIT_SAVE);
232 if( (stat(getenv("HOME"), &s)) == -1 || ! S_ISDIR(s.st_mode) ) {
233 fprintf(stderr,_("%s is not a valid HOME directory\n"), getenv("HOME") );
238 datafile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
242 rcfile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
245 atexit(free_filenames);
255 MODE_ADD_EMAIL_QUIET,
261 change_mode(int *current, int mode)
263 if(*current != MODE_CONT) {
264 fprintf(stderr, _("Cannot combine options --mutt-query, "
266 "--add-email or --add-email-quiet\n"));
274 set_filename(char **var, char *path)
279 assert(*var == NULL); /* or else we probably leak memory */
280 assert(path != NULL);
283 *var = xstrdup(path);
289 *var = strconcat(cwd, "/", path, NULL);
294 #define set_convert_var(X) do { if(mode != MODE_CONVERT) {\
295 fprintf(stderr, _("please use option --%s after --convert option\n"),\
296 long_options[option_index].name);\
303 parse_command_line(int argc, char **argv)
305 int mode = MODE_CONT;
306 char *query_string = NULL;
307 char *informat = "abook",
312 selected_item_filter = select_output_item_filter("muttq");
315 int option_index = 0;
328 static struct option long_options[] = {
329 { "help", 0, 0, 'h' },
330 { "add-email", 0, 0, OPT_ADD_EMAIL },
331 { "add-email-quiet", 0, 0, OPT_ADD_EMAIL_QUIET },
332 { "datafile", 1, 0, 'f' },
333 { "mutt-query", 1, 0, OPT_MUTT_QUERY },
334 { "config", 1, 0, 'C' },
335 { "convert", 0, 0, OPT_CONVERT },
336 { "informat", 1, 0, OPT_INFORMAT },
337 { "outformat", 1, 0, OPT_OUTFORMAT },
338 { "outformatstr", 1, 0, OPT_OUTFORMAT_STR },
339 { "infile", 1, 0, OPT_INFILE },
340 { "outfile", 1, 0, OPT_OUTFILE },
341 { "formats", 0, 0, OPT_FORMATS },
345 c = getopt_long(argc, argv, "hC:",
346 long_options, &option_index);
356 change_mode(&mode, MODE_ADD_EMAIL);
358 case OPT_ADD_EMAIL_QUIET:
359 change_mode(&mode, MODE_ADD_EMAIL_QUIET);
362 set_filename(&datafile, optarg);
363 alternative_datafile = TRUE;
366 query_string = optarg;
367 change_mode(&mode, MODE_QUERY);
370 set_filename(&rcfile, optarg);
371 alternative_rcfile = TRUE;
374 change_mode(&mode, MODE_CONVERT);
377 set_convert_var(informat);
380 if(mode != MODE_CONVERT && mode != MODE_QUERY) {
382 _("please use option --outformat after --convert or --mutt-query option\n"));
385 // ascii-name is stored, it's used to traverse
386 // e_filters[] in MODE_CONVERT (see export_file())
388 // but in case a query-compatible filter is requested
389 // try to guess right now which one it is, from u_filters[]
390 selected_item_filter = select_output_item_filter(outformat);
392 case OPT_OUTFORMAT_STR:
393 strncpy(custom_format, optarg, FORMAT_STRING_LEN);
394 custom_format[FORMAT_STRING_LEN - 1] = 0;
397 set_convert_var(infile);
400 set_convert_var(outfile);
410 // if the output format requested does not allow filtered querying
411 // (not in u_filter[]) and --convert has not been specified; bailout
412 if(! selected_item_filter.func && mode != MODE_CONVERT) {
413 printf("output format %s not supported or incompatible with --mutt-query\n", outformat);
416 if(! selected_item_filter.func)
417 selected_item_filter = select_output_item_filter("muttq");
418 else if (! strcmp(outformat, "custom")) {
419 if(! *custom_format) {
420 fprintf(stderr, _("Invalid custom format string\n"));
423 parsed_custom_format = (char *)malloc(FORMAT_STRING_LEN);
424 custom_format_fields = (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types));
425 parse_custom_format(custom_format, parsed_custom_format, custom_format_fields);
428 fprintf(stderr, _("%s: unrecognized arguments on command line\n"),
436 case MODE_ADD_EMAIL_QUIET:
439 mutt_query(query_string);
441 convert(informat, infile, outformat, outfile);
449 puts (PACKAGE " v " VERSION "\n");
450 puts (_(" -h --help show usage"));
451 puts (_(" -C --config <file> use an alternative configuration file"));
452 puts (_(" --datafile <file> use an alternative addressbook file"));
453 puts (_(" --mutt-query <string> make a query for mutt"));
454 puts (_(" --add-email "
455 "read an e-mail message from stdin and\n"
457 "add the sender to the addressbook"));
458 puts (_(" --add-email-quiet "
459 "same as --add-email but doesn't\n"
460 " require to confirm adding"));
462 puts (_(" --convert convert address book files"));
463 puts (_(" options to use with --convert:"));
464 puts (_(" --informat <format> format for input file"));
465 puts (_(" (default: abook)"));
466 puts (_(" --infile <file> source file"));
467 puts (_(" (default: stdin)"));
468 puts (_(" --outformat <format> format for output file"));
469 puts (_(" (default: text)"));
470 puts (_(" --outfile <file> destination file"));
471 puts (_(" (default: stdout)"));
472 puts (_(" --outformatstr <str> format to use for \"custom\" --outformat"));
473 puts (_(" (default: \"{nick} ({name}): {mobile}\")"));
474 puts (_(" --formats list available formats"));
483 quit_mutt_query(int status)
492 mutt_query(char *str)
496 if( str == NULL || !strcasecmp(str, "all") ) {
497 export_file("muttq", "-");
499 int search_fields[] = {NAME, EMAIL, NICK, -1};
501 if( (i = find_item(str, 0, search_fields)) < 0 ) {
502 printf("Not found\n");
503 quit_mutt_query(EXIT_FAILURE);
505 // mutt expects a leading line containing
506 // a message about the query.
507 // Others output filter supporting query (vcard, custom)
509 if(!strcmp(selected_item_filter.filtname, "muttq"))
512 e_write_item(stdout, i, selected_item_filter.func);
513 i = find_item(str, i + 1, search_fields);
517 quit_mutt_query(EXIT_SUCCESS);
527 if( load_database(datafile) ) {
528 printf(_("Cannot open database\n"));
529 quit_mutt_query(EXIT_FAILURE);
536 make_mailstr(int item)
538 char email[MAX_EMAIL_LEN];
540 char *name = strdup_printf("\"%s\"", db_name_get(item));
542 get_first_email(email, item);
545 strdup_printf("%s <%s>", name, email) :
554 print_stderr(int item)
556 fprintf (stderr, "%c", '\n');
558 if( is_valid_item(item) )
559 muttq_print_item(stderr, item);
561 struct db_enumerator e = init_db_enumerator(ENUM_SELECTED);
562 db_enumerate_items(e) {
563 muttq_print_item(stderr, e.item);
570 launch_mutt(int item)
572 char *cmd = NULL, *mailstr = NULL;
573 char *mutt_command = opt_get_str(STR_MUTT_COMMAND);
575 if(mutt_command == NULL || !*mutt_command)
578 if( is_valid_item(item) )
579 mailstr = make_mailstr(item);
581 struct db_enumerator e = init_db_enumerator(ENUM_SELECTED);
583 db_enumerate_items(e) {
586 strconcat(tmp, ",", make_mailstr(e.item), NULL):
587 strconcat(make_mailstr(e.item), NULL);
592 cmd = strconcat(mutt_command, " \'", mailstr, "\'", NULL);
595 fprintf(stderr, "cmd: %s\n", cmd);
601 * we need to make sure that curses settings are correct
607 launch_wwwbrowser(int item)
611 if( !is_valid_item(item) )
614 if(db_fget(item, URL))
615 cmd = strdup_printf("%s '%s'",
616 opt_get_str(STR_WWW_COMMAND),
617 safe_str(db_fget(item, URL)));
627 * we need to make sure that curses settings are correct
633 abook_fopen (const char *path, const char *mode)
638 stat_ok = (stat(path, &s) != -1);
640 if(strchr(mode, 'r'))
641 return (stat_ok && S_ISREG(s.st_mode)) ?
642 fopen(path, mode) : NULL;
644 return (stat_ok && S_ISDIR(s.st_mode)) ?
645 NULL : fopen(path, mode);
649 convert(char *srcformat, char *srcfile, char *dstformat, char *dstfile)
653 if( !srcformat || !srcfile || !dstformat || !dstfile ) {
654 fprintf(stderr, _("too few arguments to make conversion\n"));
655 fprintf(stderr, _("try --help\n"));
659 if( !strcasecmp(srcformat, dstformat) ) {
660 printf( _("input and output formats are the same\n"
669 init_standard_fields();
671 switch(import_file(srcformat, srcfile)) {
674 _("input format %s not supported\n"), srcformat);
678 fprintf(stderr, _("cannot read file %s\n"), srcfile);
684 switch(export_file(dstformat, dstfile)) {
687 _("output format %s not supported\n"),
693 _("cannot write file %s\n"), dstfile);
704 * --add-email handling
707 static int add_email_count = 0, add_email_found = 0;
712 if(add_email_count > 0) {
713 if(save_database() < 0) {
714 fprintf(stderr, _("cannot open %s\n"), datafile);
717 printf(_("%d item(s) added to %s\n"), add_email_count, datafile);
718 } else if (add_email_found == 0) {
719 puts(_("Valid sender address not found"));
726 quit_add_email_sig(int signal)
735 check_abook_directory();
738 init_standard_fields();
742 * we don't actually care if loading fails or not
744 load_database(datafile);
746 atexit(close_database);
748 signal(SIGINT, quit_add_email_sig);
752 add_email_add_item(int quiet, char *name, char *email)
756 if(opt_get_bool(BOOL_ADD_EMAIL_PREVENT_DUPLICATES)) {
757 int search_fields[] = { EMAIL, -1 };
758 if(find_item(email, 0, search_fields) >= 0) {
760 printf(_("Address %s already in addressbook\n"),
767 FILE *in = fopen("/dev/tty", "r");
770 fprintf(stderr, _("cannot open /dev/tty\n"
771 "you may want to use --add-email-quiet\n"));
776 printf(_("Add \"%s <%s>\" to %s? (%c/%c)\n"),
780 *S_("keybinding for yes|y"),
781 *S_("keybinding for no|n"));
782 c = tolower(getc(in));
783 if(c == *S_("keybinding for no|n")) {
787 } while(c != *S_("keybinding for yes|y"));
791 item = item_create();
792 item_fput(item, NAME, xstrdup(name));
793 item_fput(item, EMAIL, xstrdup(email));
794 add_item2database(item);
804 char *name = NULL, *email = NULL;
807 if( (fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode) ) {
808 fprintf(stderr, _("stdin is a directory or cannot stat stdin\n"));
815 line = getaline(stdin);
816 if(line && !strncasecmp("From:", line, 5) ) {
818 getname(line, &name, &email);
819 add_email_count += add_email_add_item(quiet,
825 } while( !feof(stdin) );
831 * end of --add-email handling