1 /*
2  * $Id$
3  *
4  * by JH <jheinonen@users.sourceforge.net>
5  *
6  * Copyright (C) Jaakko Heinonen
7  */
8 
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <ctype.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #ifdef HAVE_CONFIG_H
19 #	include "config.h"
20 #endif
21 #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
22 #	include <locale.h>
23 #endif
24 #include <assert.h>
25 #include "abook.h"
26 #include "gettext.h"
27 #include "ui.h"
28 #include "database.h"
29 #include "list.h"
30 #include "filter.h"
31 #include "edit.h"
32 #include "misc.h"
33 #include "options.h"
34 #include "getname.h"
35 #include "getopt.h"
36 #include "views.h"
37 #include "xmalloc.h"
38 
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);
49 
50 char *datafile = NULL;
51 static char *rcfile = NULL;
52 
53 // custom formatting
54 char custom_format[FORMAT_STRING_LEN] = "{nick} ({name}): {mobile}";
55 struct abook_output_item_filter selected_item_filter;
56 
57 bool alternative_datafile = FALSE;
58 bool alternative_rcfile = FALSE;
59 
60 
61 static int
datafile_writeable()62 datafile_writeable()
63 {
64 	FILE *f;
65 
66 	assert(datafile != NULL);
67 
68 	if( (f = fopen(datafile, "a")) == NULL)
69 		return FALSE;
70 
71 	fclose(f);
72 
73 	return TRUE;
74 }
75 
76 static void
check_abook_directory()77 check_abook_directory()
78 {
79 	struct stat s;
80 	char *dir;
81 
82 	assert(!is_ui_initialized());
83 
84 	if(alternative_datafile)
85 		return;
86 
87 	dir = strconcat(getenv("HOME"), "/" DIR_IN_HOME, NULL);
88 	assert(dir != NULL);
89 
90 	if(stat(dir, &s) == -1) {
91 		if(errno != ENOENT) {
92 			perror(dir);
93                         free(dir);
94                         exit(EXIT_FAILURE);
95 		}
96 		if(mkdir(dir, 0700) == -1) {
97 			printf(_("Cannot create directory %s\n"), dir);
98 			perror(dir);
99 			free(dir);
100 			exit(EXIT_FAILURE);
101 		}
102 	} else if(!S_ISDIR(s.st_mode)) {
103 		printf(_("%s is not a directory\n"), dir);
104 		free(dir);
105 		exit(EXIT_FAILURE);
106 	}
107 
108 	free(dir);
109 }
110 
111 static void
xmalloc_error_handler(int err)112 xmalloc_error_handler(int err)
113 {
114 	/*
115 	 * We don't try to save addressbook here because we don't know
116 	 * if it's fully loaded to memory.
117 	 */
118 	if(is_ui_initialized())
119 		close_ui();
120 
121 	fprintf(stderr, _("Memory allocation failure: %s\n"), strerror(err));
122 	exit(EXIT_FAILURE);
123 }
124 
125 static void
init_abook()126 init_abook()
127 {
128 	set_filenames();
129 	check_abook_directory();
130 	init_opts();
131 	if(load_opts(rcfile) > 0) {
132 		printf(_("Press enter to continue...\n"));
133 		fgetc(stdin);
134 	}
135 	init_default_views();
136 
137 	signal(SIGTERM, quit_abook_sig);
138 
139 	init_index();
140 
141 	if(init_ui())
142 		exit(EXIT_FAILURE);
143 
144 	umask(DEFAULT_UMASK);
145 
146 	if(!datafile_writeable()) {
147 		char *s = strdup_printf(_("File %s is not writeable"), datafile);
148 		refresh_screen();
149 		statusline_msg(s);
150 		free(s);
151 		if(load_database(datafile) || !statusline_ask_boolean(
152 					_("If you continue all changes will "
153 				"be lost. Do you want to continue?"), FALSE)) {
154 			free_opts();
155 			/*close_database();*/
156 			close_ui();
157 			exit(EXIT_FAILURE);
158 		}
159 	} else
160 		load_database(datafile);
161 
162 	refresh_screen();
163 }
164 
165 void
quit_abook(int save_db)166 quit_abook(int save_db)
167 {
168 	if(save_db)  {
169 		if(opt_get_bool(BOOL_AUTOSAVE))
170 			save_database();
171 		else if(statusline_ask_boolean(_("Save database"), TRUE))
172 			save_database();
173 	} else if(!statusline_ask_boolean(_("Quit without saving"), FALSE))
174 		return;
175 
176 	free_opts();
177 	close_database();
178 
179 	close_ui();
180 
181 	exit(EXIT_SUCCESS);
182 }
183 
184 static void
quit_abook_sig(int i)185 quit_abook_sig(int i)
186 {
187 	quit_abook(QUIT_SAVE);
188 }
189 
190 int
main(int argc,char ** argv)191 main(int argc, char **argv)
192 {
193 #if defined(HAVE_SETLOCALE) && defined(HAVE_LOCALE_H)
194 	setlocale(LC_MESSAGES, "");
195 	setlocale(LC_TIME, "");
196 	setlocale(LC_CTYPE, "");
197 	setlocale(LC_COLLATE, "");
198 #endif
199 	bindtextdomain(PACKAGE, LOCALEDIR);
200 	textdomain(PACKAGE);
201 
202 	xmalloc_set_error_handler(xmalloc_error_handler);
203 
204 	prepare_database_internals();
205 
206 	parse_command_line(argc, argv);
207 
208 	init_abook();
209 
210 	get_commands();
211 
212 	quit_abook(QUIT_SAVE);
213 
214 	return 0;
215 }
216 
217 static void
free_filenames()218 free_filenames()
219 {
220 	xfree(rcfile);
221 	xfree(datafile);
222 }
223 
224 
225 static void
set_filenames()226 set_filenames()
227 {
228 	struct stat s;
229 
230 	if( (stat(getenv("HOME"), &s)) == -1 || ! S_ISDIR(s.st_mode) ) {
231 		fprintf(stderr,_("%s is not a valid HOME directory\n"), getenv("HOME") );
232 		exit(EXIT_FAILURE);
233 	}
234 
235 	if(!datafile)
236 		datafile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
237 				DATAFILE, NULL);
238 
239 	if(!rcfile)
240 		rcfile = strconcat(getenv("HOME"), "/" DIR_IN_HOME "/"
241 				RCFILE, NULL);
242 
243 	atexit(free_filenames);
244 }
245 
246 /*
247  * CLI
248  */
249 
250 enum {
251 	MODE_CONT,
252 	MODE_ADD_EMAIL,
253 	MODE_ADD_EMAIL_QUIET,
254 	MODE_QUERY,
255 	MODE_CONVERT
256 };
257 
258 static void
change_mode(int * current,int mode)259 change_mode(int *current, int mode)
260 {
261 	if(*current != MODE_CONT) {
262 		fprintf(stderr, _("Cannot combine options --mutt-query, "
263 				"--convert, "
264 				"--add-email or --add-email-quiet\n"));
265 		exit(EXIT_FAILURE);
266 	}
267 
268 	*current = mode;
269 }
270 
271 void
set_filename(char ** var,char * path)272 set_filename(char **var, char *path)
273 {
274 	char *cwd;
275 
276 	assert(var != NULL);
277 	assert(*var == NULL); /* or else we probably leak memory */
278 	assert(path != NULL);
279 
280 	if(*path == '/') {
281 		*var = xstrdup(path);
282 		return;
283 	}
284 
285 	cwd = my_getcwd();
286 
287 	*var = strconcat(cwd, "/", path, NULL);
288 
289 	free(cwd);
290 }
291 
292 #define set_convert_var(X) do { if(mode != MODE_CONVERT) {\
293 	fprintf(stderr, _("please use option --%s after --convert option\n"),\
294 			long_options[option_index].name);\
295 		exit(EXIT_FAILURE);\
296 	} else\
297 		X = optarg;\
298 	} while(0)
299 
300 static void
parse_command_line(int argc,char ** argv)301 parse_command_line(int argc, char **argv)
302 {
303 	int mode = MODE_CONT;
304 	char *query_string = NULL;
305 	char *informat = "abook",
306 		*outformat = "text",
307 		*infile = "-",
308 		*outfile = "-";
309 	int c;
310 	selected_item_filter = select_output_item_filter("muttq");
311 
312 	for(;;) {
313 		int option_index = 0;
314 		enum {
315 			OPT_ADD_EMAIL,
316 			OPT_ADD_EMAIL_QUIET,
317 			OPT_MUTT_QUERY,
318 			OPT_CONVERT,
319 			OPT_INFORMAT,
320 			OPT_OUTFORMAT,
321 			OPT_OUTFORMAT_STR,
322 			OPT_INFILE,
323 			OPT_OUTFILE,
324 			OPT_FORMATS
325 		};
326 		static struct option long_options[] = {
327 			{ "help", 0, 0, 'h' },
328 			{ "add-email", 0, 0, OPT_ADD_EMAIL },
329 			{ "add-email-quiet", 0, 0, OPT_ADD_EMAIL_QUIET },
330 			{ "datafile", 1, 0, 'f' },
331 			{ "mutt-query", 1, 0, OPT_MUTT_QUERY },
332 			{ "config", 1, 0, 'C' },
333 			{ "convert", 0, 0, OPT_CONVERT },
334 			{ "informat", 1, 0, OPT_INFORMAT },
335 			{ "outformat", 1, 0, OPT_OUTFORMAT },
336 			{ "outformatstr", 1, 0, OPT_OUTFORMAT_STR },
337 			{ "infile", 1, 0, OPT_INFILE },
338 			{ "outfile", 1, 0, OPT_OUTFILE },
339 			{ "formats", 0, 0, OPT_FORMATS },
340 			{ 0, 0, 0, 0 }
341 		};
342 
343 		c = getopt_long(argc, argv, "hC:f:",
344 				long_options, &option_index);
345 
346 		if(c == -1)
347 			break;
348 
349 		switch(c) {
350 			case 'h':
351 				show_usage();
352 				exit(EXIT_SUCCESS);
353 			case OPT_ADD_EMAIL:
354 				change_mode(&mode, MODE_ADD_EMAIL);
355 				break;
356 			case OPT_ADD_EMAIL_QUIET:
357 				change_mode(&mode, MODE_ADD_EMAIL_QUIET);
358 				break;
359 			case 'f':
360 				set_filename(&datafile, optarg);
361 				alternative_datafile = TRUE;
362 				break;
363 			case OPT_MUTT_QUERY:
364 				query_string = optarg;
365 				change_mode(&mode, MODE_QUERY);
366 				break;
367 			case 'C':
368 				set_filename(&rcfile, optarg);
369 				alternative_rcfile = TRUE;
370 				break;
371 			case OPT_CONVERT:
372 				change_mode(&mode, MODE_CONVERT);
373 				break;
374 			case OPT_INFORMAT:
375 				set_convert_var(informat);
376 				break;
377 			case OPT_OUTFORMAT:
378 				if(mode != MODE_CONVERT && mode != MODE_QUERY) {
379 				  fprintf(stderr,
380 					  _("please use option --outformat after --convert or --mutt-query option\n"));
381 				  exit(EXIT_FAILURE);
382 				}
383 				// ascii-name is stored, it's used to traverse
384 				// e_filters[] in MODE_CONVERT (see export_file())
385 				outformat = optarg;
386 				// but in case a query-compatible filter is requested
387 				// try to guess right now which one it is, from u_filters[]
388 				selected_item_filter = select_output_item_filter(outformat);
389 				break;
390 			case OPT_OUTFORMAT_STR:
391 				strncpy(custom_format, optarg, FORMAT_STRING_LEN);
392 				custom_format[FORMAT_STRING_LEN - 1] = 0;
393 				break;
394 			case OPT_INFILE:
395 				set_convert_var(infile);
396 				break;
397 			case OPT_OUTFILE:
398 				set_convert_var(outfile);
399 				break;
400 			case OPT_FORMATS:
401 				print_filters();
402 				exit(EXIT_SUCCESS);
403 			default:
404 				exit(EXIT_FAILURE);
405 		}
406 	}
407 
408 	// if the output format requested does not allow filtered querying
409 	// (not in u_filter[]) and --convert has not been specified; bailout
410 	if(! selected_item_filter.func && mode != MODE_CONVERT) {
411 	  printf("output format %s not supported or incompatible with --mutt-query\n", outformat);
412 	  exit(EXIT_FAILURE);
413 	}
414 	if(! selected_item_filter.func)
415 		selected_item_filter = select_output_item_filter("muttq");
416 	else if (! strcmp(outformat, "custom")) {
417 		if(! *custom_format) {
418 			fprintf(stderr, _("Invalid custom format string\n"));
419 			exit(EXIT_FAILURE);
420 		}
421 	}
422 	if(optind < argc) {
423 		fprintf(stderr, _("%s: unrecognized arguments on command line\n"),
424 				argv[0]);
425 		exit(EXIT_FAILURE);
426 	}
427 
428 	switch(mode) {
429 		case MODE_ADD_EMAIL:
430 			add_email(0);
431 		case MODE_ADD_EMAIL_QUIET:
432 			add_email(1);
433 		case MODE_QUERY:
434 			mutt_query(query_string);
435 		case MODE_CONVERT:
436 			convert(informat, infile, outformat, outfile);
437 	}
438 }
439 
440 
441 static void
show_usage()442 show_usage()
443 {
444 	puts	(PACKAGE " v" VERSION "\n");
445 	puts	(_("     -h	--help				show usage"));
446 	puts	(_("     -C	--config	<file>		use an alternative configuration file"));
447 	puts	(_("     -f	--datafile	<file>		use an alternative addressbook file"));
448 	puts	(_("	--mutt-query	<string>	make a query for mutt"));
449 	puts	(_("	--add-email			"
450 			"read an e-mail message from stdin and\n"
451 		"					"
452 		"add the sender to the addressbook"));
453 	puts	(_("	--add-email-quiet		"
454 		"same as --add-email but doesn't\n"
455 		"					require to confirm adding"));
456 	putchar('\n');
457 	puts	(_("	--convert			convert address book files"));
458 	puts	(_("	options to use with --convert:"));
459 	puts	(_("	--informat	<format>	format for input file"));
460 	puts	(_("					(default: abook)"));
461 	puts	(_("	--infile	<file>		source file"));
462 	puts	(_("					(default: stdin)"));
463 	puts	(_("	--outformat	<format>	format for output file"));
464 	puts	(_("					(default: text)"));
465 	puts	(_("	--outfile	<file>		destination file"));
466 	puts	(_("					(default: stdout)"));
467 	puts	(_("	--outformatstr	<str>   	format to use for \"custom\" --outformat"));
468 	puts	(_("					(default: \"{nick} ({name}): {mobile}\")"));
469 	puts	(_("	--formats			list available formats"));
470 }
471 
472 /*
473  * end of CLI
474  */
475 
476 
477 static void
quit_mutt_query(int status)478 quit_mutt_query(int status)
479 {
480 	close_database();
481 	free_opts();
482 
483 	exit(status);
484 }
485 
486 static void
mutt_query(char * str)487 mutt_query(char *str)
488 {
489 	init_mutt_query();
490 
491 	if( str == NULL || !strcasecmp(str, "all") ) {
492 		export_file("muttq", "-");
493 	} else {
494 		int search_fields[] = {NAME, EMAIL, NICK, -1};
495 		int i;
496 		if( (i = find_item(str, 0, search_fields)) < 0 ) {
497 			printf("Not found\n");
498 			quit_mutt_query(EXIT_FAILURE);
499 		}
500 		// mutt expects a leading line containing
501 		// a message about the query.
502 		// Others output filter supporting query (vcard, custom)
503 		// don't needs this.
504 		if(!strcmp(selected_item_filter.filtname, "muttq"))
505 			putchar('\n');
506 		while(i >= 0) {
507 			e_write_item(stdout, i, selected_item_filter.func);
508 			i = find_item(str, i + 1, search_fields);
509 		}
510 	}
511 
512 	quit_mutt_query(EXIT_SUCCESS);
513 }
514 
515 static void
init_mutt_query()516 init_mutt_query()
517 {
518 	set_filenames();
519 	init_opts();
520 	load_opts(rcfile);
521 
522 	if( load_database(datafile) ) {
523 		printf(_("Cannot open database\n"));
524 		quit_mutt_query(EXIT_FAILURE);
525 		exit(EXIT_FAILURE);
526 	}
527 }
528 
529 
530 static char *
make_mailstr(int item)531 make_mailstr(int item)
532 {
533 	char email[MAX_EMAIL_LEN];
534 	char *ret;
535 	char *name = strdup_printf("\"%s\"", db_name_get(item));
536 
537 	get_first_email(email, item);
538 
539 	ret = *email ?
540 		strdup_printf("%s <%s>", name, email) :
541 		xstrdup(name);
542 
543 	free(name);
544 
545 	return ret;
546 }
547 
548 void
print_stderr(int item)549 print_stderr(int item)
550 {
551 	fprintf (stderr, "%c", '\n');
552 
553 	if( is_valid_item(item) )
554 		muttq_print_item(stderr, item);
555 	else {
556 		struct db_enumerator e = init_db_enumerator(ENUM_SELECTED);
557 		db_enumerate_items(e) {
558 			muttq_print_item(stderr, e.item);
559 		}
560 	}
561 
562 }
563 
564 void
launch_mutt(int item)565 launch_mutt(int item)
566 {
567 	char *cmd = NULL, *mailstr = NULL;
568 	char *mutt_command = opt_get_str(STR_MUTT_COMMAND);
569 
570 	if(mutt_command == NULL || !*mutt_command)
571 		return;
572 
573 	if( is_valid_item(item) )
574 		mailstr = make_mailstr(item);
575 	else {
576 		struct db_enumerator e = init_db_enumerator(ENUM_SELECTED);
577 		char *tmp = NULL;
578 		db_enumerate_items(e) {
579 			tmp = mailstr;
580 			mailstr = tmp ?
581 				strconcat(tmp, ",", make_mailstr(e.item), NULL):
582 				strconcat(make_mailstr(e.item), NULL);
583 			free(tmp);
584 		}
585 	}
586 
587 	cmd = strconcat(mutt_command, " \'", mailstr, "\'", NULL);
588 	free(mailstr);
589 #ifdef DEBUG
590 	fprintf(stderr, "cmd: %s\n", cmd);
591 #endif
592 	system(cmd);
593 	free(cmd);
594 
595 	/*
596 	 * we need to make sure that curses settings are correct
597 	 */
598 	ui_init_curses();
599 }
600 
601 void
launch_wwwbrowser(int item)602 launch_wwwbrowser(int item)
603 {
604 	char *cmd = NULL;
605 
606 	if( !is_valid_item(item) )
607 		return;
608 
609 	if(db_fget(item, URL))
610 		cmd = strdup_printf("%s '%s'",
611 				opt_get_str(STR_WWW_COMMAND),
612 				safe_str(db_fget(item, URL)));
613 	else
614 		return;
615 
616 	if ( cmd )
617 		system(cmd);
618 
619 	free(cmd);
620 
621 	/*
622 	 * we need to make sure that curses settings are correct
623 	 */
624 	ui_init_curses();
625 }
626 
627 FILE *
abook_fopen(const char * path,const char * mode)628 abook_fopen (const char *path, const char *mode)
629 {
630 	struct stat s;
631 	bool stat_ok;
632 
633 	stat_ok = (stat(path, &s) != -1);
634 
635 	if(strchr(mode, 'r'))
636 		return (stat_ok && S_ISREG(s.st_mode)) ?
637 			fopen(path, mode) : NULL;
638 	else
639 		return (stat_ok && S_ISDIR(s.st_mode)) ?
640 			NULL : fopen(path, mode);
641 }
642 
643 static void
convert(char * srcformat,char * srcfile,char * dstformat,char * dstfile)644 convert(char *srcformat, char *srcfile, char *dstformat, char *dstfile)
645 {
646 	int ret=0;
647 
648 	if( !srcformat || !srcfile || !dstformat || !dstfile ) {
649 		fprintf(stderr, _("too few arguments to make conversion\n"));
650 		fprintf(stderr, _("try --help\n"));
651 	}
652 
653 #ifndef DEBUG
654 	if( !strcasecmp(srcformat, dstformat) ) {
655 		printf(	_("input and output formats are the same\n"
656 			"exiting...\n"));
657 		exit(EXIT_FAILURE);
658 	}
659 #endif
660 
661 	set_filenames();
662 	init_opts();
663 	load_opts(rcfile);
664 	init_standard_fields();
665 
666 	switch(import_file(srcformat, srcfile)) {
667 		case -1:
668 			fprintf(stderr,
669 				_("input format %s not supported\n"), srcformat);
670 			ret = 1;
671 			break;
672 		case 1:
673 			fprintf(stderr, _("cannot read file %s\n"), srcfile);
674 			ret = 1;
675 			break;
676 	}
677 
678 	if(!ret)
679 		switch(export_file(dstformat, dstfile)) {
680 			case -1:
681 				fprintf(stderr,
682 					_("output format %s not supported\n"),
683 					dstformat);
684 				ret = 1;
685 				break;
686 			case 1:
687 				fprintf(stderr,
688 					_("cannot write file %s\n"), dstfile);
689 				ret = 1;
690 				break;
691 		}
692 
693 	close_database();
694 	free_opts();
695 	exit(ret);
696 }
697 
698 /*
699  * --add-email handling
700  */
701 
702 static int add_email_count = 0, add_email_found = 0;
703 
704 static void
quit_add_email()705 quit_add_email()
706 {
707 	if(add_email_count > 0) {
708 		if(save_database() < 0) {
709 			fprintf(stderr, _("cannot open %s\n"), datafile);
710 			exit(EXIT_FAILURE);
711 		}
712 		printf(_("%d item(s) added to %s\n"), add_email_count, datafile);
713 	} else if (add_email_found == 0) {
714 		puts(_("Valid sender address not found"));
715 	}
716 
717 	exit(EXIT_SUCCESS);
718 }
719 
720 static void
quit_add_email_sig(int signal)721 quit_add_email_sig(int signal)
722 {
723 	quit_add_email();
724 }
725 
726 static void
init_add_email()727 init_add_email()
728 {
729 	set_filenames();
730 	check_abook_directory();
731 	init_opts();
732 	load_opts(rcfile);
733 	init_standard_fields();
734 	atexit(free_opts);
735 
736 	/*
737 	 * we don't actually care if loading fails or not
738 	 */
739 	load_database(datafile);
740 
741 	atexit(close_database);
742 
743 	signal(SIGINT, quit_add_email_sig);
744 }
745 
746 static int
add_email_add_item(int quiet,char * name,char * email)747 add_email_add_item(int quiet, char *name, char *email)
748 {
749 	list_item item;
750 
751 	if(opt_get_bool(BOOL_ADD_EMAIL_PREVENT_DUPLICATES)) {
752 		int search_fields[] = { EMAIL, -1 };
753 		if(find_item(email, 0, search_fields) >= 0) {
754 			if(!quiet)
755 				printf(_("Address %s already in addressbook\n"),
756 						email);
757 			return 0;
758 		}
759 	}
760 
761 	if(!quiet) {
762 		FILE *in = fopen("/dev/tty", "r");
763 		char c;
764 		if(!in) {
765 			fprintf(stderr, _("cannot open /dev/tty\n"
766 				"you may want to use --add-email-quiet\n"));
767 			exit(EXIT_FAILURE);
768 		}
769 
770 		do {
771 			printf(_("Add \"%s <%s>\" to %s? (%c/%c)\n"),
772 					name,
773 					email,
774 					datafile,
775 					*S_("keybinding for yes|y"),
776 					*S_("keybinding for no|n"));
777 			c = tolower(getc(in));
778 			if(c == *S_("keybinding for no|n")) {
779 				fclose(in);
780 				return 0;
781 			}
782 		} while(c != *S_("keybinding for yes|y"));
783 		fclose(in);
784 	}
785 
786 	item = item_create();
787 	item_fput(item, NAME, xstrdup(name));
788 	item_fput(item, EMAIL, xstrdup(email));
789 	add_item2database(item);
790 	item_free(&item);
791 
792 	return 1;
793 }
794 
795 static void
add_email(int quiet)796 add_email(int quiet)
797 {
798 	char *line;
799 	char *name = NULL, *email = NULL;
800 	struct stat s;
801 
802 	if( (fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode) ) {
803 		fprintf(stderr, _("stdin is a directory or cannot stat stdin\n"));
804 		exit(EXIT_FAILURE);
805 	}
806 
807 	init_add_email();
808 
809 	do {
810 		line = getaline(stdin);
811 		if(line && !strncasecmp("From:", line, 5) ) {
812 			add_email_found++;
813 			getname(line, &name, &email);
814 			add_email_count += add_email_add_item(quiet,
815 					name, email);
816 			xfree(name);
817 			xfree(email);
818 		}
819 		xfree(line);
820 	} while( !feof(stdin) );
821 
822 	quit_add_email();
823 }
824 
825 /*
826  * end of --add-email handling
827  */
828