Add parsing of -l option
diff --git a/ChangeLog b/ChangeLog
index b33fd02..997d317 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2012-09-27  Petr Machata  <pmachata@redhat.com>
 
+	* options.h (struct options_t.export_filter): New field.
+	* options.c (parse_filter): Take argument OPERATORS.
+	(recursive_parse_chain): Likewise.
+	(slist_chase_end): New function.
+	(process_options): Handle 'l' option, initialize export_filter.
+	* ltrace.1: Explain -l.
+	* testsuite/ltrace.main/main-vfork.exp: Update use of -l.
+	* testsuite/ltrace.main/main.exp: Likewise.
+	* testsuite/ltrace.main/parameters-lib.c (func_printf): New function.
+	* testsuite/ltrace.main/parameters.c: Call it instead of printf.
+	* testsuite/ltrace.main/parameters.conf: Declare it.
+	* testsuite/ltrace.main/parameters.exp: Update expectations and -l.
+
+2012-09-27  Petr Machata  <pmachata@redhat.com>
+
 	* library.h (library_symbol_named_cb): New function.
 	* library.c (library_symbol_named_cb): Implemented here.
 
diff --git a/ltrace.1 b/ltrace.1
index fb64289..16cb03b 100644
--- a/ltrace.1
+++ b/ltrace.1
@@ -7,7 +7,7 @@
 
 .SH SYNOPSIS
 .B ltrace
-.I "[-bCfghiLrStttV] [-a column] [-A maxelts] [-D level] [-e expr] [-l filename] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-w count] [-x extern] ... [--align=column] [--debug=level] [--demangle] [--help] [--indent=nr] [--library=filename] [--no-signals] [--output=filename] [--version] [--where=NR] [command [arg ...]]"
+.I "[-bCfghiLrStttV] [-a column] [-A maxelts] [-D level] [-e expr] [-l library_pattern] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-w count] [-x extern] ... [--align=column] [--debug=level] [--demangle] [--help] [--indent=nr] [--library=library_pattern] [--no-signals] [--output=filename] [--version] [--where=NR] [command [arg ...]]"
 
 .SH DESCRIPTION
 .B ltrace
@@ -95,16 +95,16 @@
 .I \-i
 Print the instruction pointer at the time of the library call.
 .TP
-.I \-l, \-\-library filename
-Display only the symbols included in the library
-.I filename.
-Up to 30 library names can be specified with several instances
-of this option.
+.I \-l, \-\-library library_pattern
+Display only the symbols implemented by libraries that match
+.I library_pattern.
+Multiple library patters can be specified with several instances of
+this option.  Syntax of library_pattern is described in section
+\fBFILTER EXPRESSIONS\fR.
 .TP
 .I \-L
-DON'T display library calls (use it with the
-.I \-S
-option).
+When no -e option is given, don't assume the default action of
+\fB@MAIN\fR.
 .TP
 .I \-n, \-\-indent nr
 Indent trace output by
diff --git a/options.c b/options.c
index 8b73233..40bac34 100644
--- a/options.c
+++ b/options.c
@@ -74,7 +74,7 @@
 		"  -F, --config=FILE   load alternate configuration file (may be repeated).\n"
 		"  -h, --help          display this help and exit.\n"
 		"  -i                  print instruction pointer at time of library call.\n"
-		"  -l, --library=FILE  print library calls from this library only.\n"
+		"  -l, --library=FILE  only trace symbols implemented by this library.\n"
 		"  -L                  do NOT display library calls.\n"
 		"  -n, --indent=NR     indent output by NR spaces for each call level nesting.\n"
 		"  -o, --output=FILE   write the trace output to that file.\n"
@@ -257,7 +257,7 @@
 }
 
 static int
-parse_filter(struct filter *filt, char *expr)
+parse_filter(struct filter *filt, char *expr, int operators)
 {
 	/* Filter is a chain of sym@lib rules separated by '-' or '+'.
 	 * If the filter expression starts with '-', the missing
@@ -266,7 +266,7 @@
 	enum filter_rule_type type = FR_ADD;
 
 	while (*expr != 0) {
-		size_t s = strcspn(expr, "@-+");
+		size_t s = strcspn(expr, "-+@" + (operators ? 0 : 2));
 		char *symname = expr;
 		char *libname;
 		char *next = expr + s + 1;
@@ -285,7 +285,7 @@
 		} else {
 			assert(expr[s] == '@');
 			expr[s] = 0;
-			s = strcspn(next, "-+");
+			s = strcspn(next, "-+" + (operators ? 0 : 2));
 			if (s == 0) {
 				libname = "*";
 				expr = next;
@@ -357,7 +357,7 @@
 }
 
 static struct filter *
-recursive_parse_chain(char *expr)
+recursive_parse_chain(char *expr, int operators)
 {
 	struct filter *filt = malloc(sizeof(*filt));
 	if (filt == NULL) {
@@ -367,7 +367,7 @@
 	}
 
 	filter_init(filt);
-	if (parse_filter(filt, expr) < 0) {
+	if (parse_filter(filt, expr, operators) < 0) {
 		fprintf(stderr, "Filter '%s' will be ignored.\n", expr);
 		free(filt);
 		filt = NULL;
@@ -376,6 +376,14 @@
 	return filt;
 }
 
+static struct filter **
+slist_chase_end(struct filter **begin)
+{
+	for (; *begin != NULL; begin = &(*begin)->next)
+		;
+	return begin;
+}
+
 static void
 parse_filter_chain(const char *expr, struct filter **retp)
 {
@@ -389,10 +397,7 @@
 	if (str[0] == '!')
 		str[0] = '-';
 
-	struct filter **tailp;
-	for (tailp = retp; *tailp != NULL; tailp = &(*tailp)->next)
-		;
-	*tailp = recursive_parse_chain(str);
+	*slist_chase_end(retp) = recursive_parse_chain(str, 1);
 }
 
 char **
@@ -499,10 +504,16 @@
 		case 'i':
 			opt_i++;
 			break;
-		case 'l':
-			// XXX TODO
-			fprintf(stderr, "-l support not yet implemented\n");
+
+		case 'l': {
+			size_t patlen = strlen(optarg);
+			char buf[patlen + 2];
+			sprintf(buf, "@%s", optarg);
+			*slist_chase_end(&options.export_filter)
+				= recursive_parse_chain(buf, 0);
 			break;
+		}
+
 		case 'L':
 			libcalls = 0;
 			break;
@@ -607,13 +618,21 @@
 		opt_F = egg;
 	}
 
-	/* Set default filter.  Use @MAIN for now, as that's what
-	 * ltrace used to have in the past.  XXX Maybe we should make
-	 * this "*" instead.  */
-	if (options.plt_filter == NULL && libcalls) {
+	/* If neither -e, nor -l, nor -L are used, set default -e.
+	 * Use @MAIN for now, as that's what ltrace used to have in
+	 * the past.  XXX Maybe we should make this "*" instead.  */
+	if (libcalls
+	    && options.plt_filter == NULL
+	    && options.export_filter == NULL) {
 		parse_filter_chain("@MAIN", &options.plt_filter);
 		options.hide_caller = 1;
 	}
+	if (!libcalls && options.plt_filter != NULL) {
+		fprintf(stderr,
+			"%s: Option -L can't be used with -e or -l.\n",
+			progname);
+		err_usage();
+	}
 
 	if (!opt_p && argc < 1) {
 		fprintf(stderr, "%s: too few arguments\n", progname);
diff --git a/options.h b/options.h
index 0806928..176ce90 100644
--- a/options.h
+++ b/options.h
@@ -21,6 +21,11 @@
 #endif /* defined(HAVE_LIBUNWIND) */
 	struct filter *plt_filter;
 	struct filter *static_filter;
+
+	/* A filter matching library names of libraries, whose
+	 * exported symbols we wish to trace.  */
+	struct filter *export_filter;
+
 	int hide_caller; /* Whether caller library should be hidden.  */
 };
 extern struct options_t options;
diff --git a/testsuite/ltrace.main/main-threaded.exp b/testsuite/ltrace.main/main-threaded.exp
index 5539805..4f6c25d 100644
--- a/testsuite/ltrace.main/main-threaded.exp
+++ b/testsuite/ltrace.main/main-threaded.exp
@@ -19,7 +19,7 @@
 }
 
 # set options for ltrace.
-ltrace_options "-l" "$lib_sl" "-f"
+ltrace_options "-l" "lib$testfile.so" "-f"
 
 # Run PUT for ltarce.
 set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
diff --git a/testsuite/ltrace.main/main-vfork.exp b/testsuite/ltrace.main/main-vfork.exp
index 299c5e0..989012d 100644
--- a/testsuite/ltrace.main/main-vfork.exp
+++ b/testsuite/ltrace.main/main-vfork.exp
@@ -19,7 +19,7 @@
 }
 
 # set options for ltrace.
-ltrace_options "-l" "$lib_sl" "-f"
+ltrace_options "-l" "lib$testfile.so" "-f" "-evfork"
 
 # Run PUT for ltarce.
 set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
diff --git a/testsuite/ltrace.main/main.exp b/testsuite/ltrace.main/main.exp
index 9e126bc..50c5353 100644
--- a/testsuite/ltrace.main/main.exp
+++ b/testsuite/ltrace.main/main.exp
@@ -19,7 +19,7 @@
 }
 
 # set options for ltrace.
-ltrace_options "-l" "$objdir/$subdir/libmain.so"
+ltrace_options "-l" "libmain.so"
 
 # Run PUT for ltarce.
 set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index 750075e..04effaf 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -456,3 +456,8 @@
 {
 	return e;
 }
+
+void
+func_printf(char *format, ...)
+{
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index 44d54b2..ae8e17b 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -182,10 +182,11 @@
 		 15, 16, 'B', 18.0, 19.0, 20.0,
 		 21, 22.0, 23.0, 24.0, 25.0);
 
-  printf("sotnuh %d %ld %g %c\n", 5, 6L, 1.5, 'X');
-  printf("sotnuh1 %d %ld %hd\n", 5, 6L, (short)7);
-  printf("sotnuh2 %s %10s %10s\n", "a string", "a trimmed string", "short");
-  printf("many_args"
+  void func_printf(char *format, ...);
+  func_printf("sotnuh %d %ld %g %c\n", 5, 6L, 1.5, 'X');
+  func_printf("sotnuh1 %d %ld %hd\n", 5, 6L, (short)7);
+  func_printf("sotnuh2 %s %10s %10s\n", "a string", "a trimmed string", "short");
+  func_printf("many_args"
 	 "%d %d %ld %g %c %d %g "
 	 "%c %d %g %d %g %c %d "
 	 "%hd %d %c %g %g %g "
@@ -195,7 +196,7 @@
 	 (short)15, 16, 'B', 18.0, 19.0, 20.0,
 	 21L, 22.0, 23.0, 24.0, 25.0);
 
-  printf("sotnuh3 %*s\n", 4, "a trimmed string");
+  func_printf("sotnuh3 %*s\n", 4, "a trimmed string");
 
   void func_lens(int, long, short, long);
   func_lens(22, 23, 24, 25);
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index 0531a6a..743237f 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -22,7 +22,7 @@
 struct(char,char,long,long) func_struct_large2(struct(char,char,long,long), struct(char,char,long,long));
 struct(long,long,char,char) func_struct_large3(struct(long,long,char,char), struct(long,long,char,char));
 void func_many_args(int, int, long, double, char, int, float, char, int, double, int, double, char, int, short, int, char, float, float, double, long, float, float, float, float);
-int printf(format);
+void func_printf(format);
 void func_lens(octal, octal(long), hex(short), hex(long));
 bool(int) func_bool(int, bool(int));
 void func_hide(int, hide(int), hide(int), int, hide(int), int);
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index badbe6b..367214f 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -20,7 +20,7 @@
 }
 
 # set options for ltrace.
-ltrace_options "-l" "$objdir/$subdir/libparameters.so" "-F" "$srcdir/$subdir/parameters.conf"
+ltrace_options "-l" "libparameters.so" "-F" "$srcdir/$subdir/parameters.conf"
 
 # Run PUT for ltarce.
 set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
@@ -100,19 +100,19 @@
 set pattern "func_many_args(1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)"
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
 
-set pattern "printf(\\\"sotnuh %d %ld %g %c.n\\\", 5, 6, 1.500*, 'X')"
+set pattern "func_printf(\\\"sotnuh %d %ld %g %c.n\\\", 5, 6, 1.500*, 'X')"
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
 
-set pattern "printf(\\\"sotnuh1 %d %ld %hd.n\\\", 5, 6, 7)"
+set pattern "func_printf(\\\"sotnuh1 %d %ld %hd.n\\\", 5, 6, 7)"
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
 
-set pattern "printf(\\\"sotnuh2 %s %10s %10s.n\\\", \\\"a string\\\", \\\"a trimmed \\\", \\\"short\\\")"
+set pattern "func_printf(\\\"sotnuh2 %s %10s %10s.n\\\", \\\"a string\\\", \\\"a trimmed \\\", \\\"short\\\")"
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
 
-set pattern "printf(\\\"sotnuh3 %.s.n\\\", 4, \\\"a tr\\\")"
+set pattern "func_printf(\\\"sotnuh3 %.s.n\\\", 4, \\\"a tr\\\")"
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
 
-set pattern "printf(\\\"many_args%d %d %ld %g %c %d %g .*, 1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)"
+set pattern "func_printf(\\\"many_args%d %d %ld %g %c %d %g .*, 1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)"
 ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
 
 set pattern "func_lens(026, 027, 0x18, 0x19)"