1 /*
2 * This file is part of ltrace.
3 * Copyright (C) 2012, 2013, 2014 Petr Machata, Red Hat Inc.
4 * Copyright (C) 2009,2010 Joe Damato
5 * Copyright (C) 1998,1999,2002,2003,2004,2007,2008,2009 Juan Cespedes
6 * Copyright (C) 2006 Ian Wienand
7 * Copyright (C) 2006 Steve Fink
8 * Copyright (C) 2006 Paul Gilliam, IBM Corporation
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301 USA
24 */
25
26 #include "config.h"
27
28 #include <sys/ioctl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <assert.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <getopt.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include "common.h"
42 #include "filter.h"
43 #include "glob.h"
44 #include "demangle.h"
45
46 struct options_t options = {
47 .align = DEFAULT_ALIGN, /* alignment column for results */
48 .user = NULL, /* username to run command as */
49 .syscalls = 0, /* display syscalls */
50 #ifdef USE_DEMANGLE
51 .demangle = 0, /* Demangle low-level symbol names */
52 #endif
53 .indent = 0, /* indent output according to program flow */
54 .output = NULL, /* output to a specific file */
55 .summary = 0, /* Report a summary on program exit */
56 .debug = 0, /* debug */
57 .arraylen = DEFAULT_ARRAYLEN, /* maximum # array elements to print */
58 .strlen = DEFAULT_STRLEN, /* maximum # of bytes printed in strings */
59 .follow = 0, /* trace child processes */
60 };
61
62 static char *progname; /* Program name (`ltrace') */
63 int opt_i = 0; /* instruction pointer */
64 int opt_r = 0; /* print relative timestamp */
65 int opt_t = 0; /* print absolute timestamp */
66 int opt_T = 0; /* show the time spent inside each call */
67
68 /* List of pids given to option -p: */
69 struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */
70
71 /* Vector of struct opt_F_t. */
72 struct vect opt_F;
73
74 static void
err_usage(void)75 err_usage(void) {
76 fprintf(stderr, "Try `%s --help' for more information.\n", progname);
77 exit(1);
78 }
79
80 static void
usage(void)81 usage(void) {
82 fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n"
83 "Trace library calls of a given program.\n\n"
84 " -a, --align=COLUMN align return values in a secific column.\n"
85 " -A MAXELTS maximum number of array elements to print.\n"
86 " -b, --no-signals don't print signals.\n"
87 " -c count time and calls, and report a summary on exit.\n"
88 # ifdef USE_DEMANGLE
89 " -C, --demangle decode low-level symbol names into user-level names.\n"
90 # endif
91 " -D, --debug=MASK enable debugging (see -Dh or --debug=help).\n"
92 " -Dh, --debug=help show help on debugging.\n"
93 " -e FILTER modify which library calls to trace.\n"
94 " -f trace children (fork() and clone()).\n"
95 " -F, --config=FILE load alternate configuration file (may be repeated).\n"
96 " -h, --help display this help and exit.\n"
97 " -i print instruction pointer at time of library call.\n"
98 " -l, --library=LIBRARY_PATTERN only trace symbols implemented by this library.\n"
99 " -L do NOT display library calls.\n"
100 " -n, --indent=NR indent output by NR spaces for each call level nesting.\n"
101 " -o, --output=FILENAME write the trace output to file with given name.\n"
102 " -p PID attach to the process with the process ID pid.\n"
103 " -r print relative timestamps.\n"
104 " -s STRSIZE specify the maximum string size to print.\n"
105 " -S trace system calls as well as library calls.\n"
106 " -t, -tt, -ttt print absolute timestamps.\n"
107 " -T show the time spent inside each call.\n"
108 " -u USERNAME run command with the userid, groupid of username.\n"
109 " -V, --version output version information and exit.\n"
110 #if defined(HAVE_UNWINDER)
111 " -w, --where=NR print backtrace showing NR stack frames at most.\n"
112 #endif /* defined(HAVE_UNWINDER) */
113 " -x FILTER modify which static functions to trace.\n"
114 "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n",
115 progname);
116 }
117
118 static void
usage_debug(void)119 usage_debug(void) {
120 fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname);
121 fprintf(stdout,
122 "\n"
123 " number ref. in source description\n"
124 " 1 general Generally helpful progress information\n"
125 " 10 event Shows every event received by a traced process\n"
126 " 20 process Shows actions carried upon a traced processes\n"
127 " 40 function Shows every entry to internal functions\n"
128 "\n"
129 "Debugging options are mixed using bitwise-or.\n"
130 "Note that the meanings and values are subject to change.\n"
131 "Also note that these values are used inconsistently in ltrace, and the\n"
132 "only debuglevel that you can rely on is -D77 that will show everything.\n"
133 );
134 }
135
136 static char *
search_for_command(char * filename)137 search_for_command(char *filename) {
138 static char pathname[PATH_MAX];
139 char *path;
140 int m, n;
141
142 if (strchr(filename, '/')) {
143 return filename;
144 }
145 for (path = getenv("PATH"); path && *path; path += m) {
146 if (strchr(path, ':')) {
147 n = strchr(path, ':') - path;
148 m = n + 1;
149 } else {
150 m = n = strlen(path);
151 }
152 if (n + strlen(filename) + 1 >= PATH_MAX) {
153 fprintf(stderr, "Error: filename too long.\n");
154 exit(1);
155 }
156 strncpy(pathname, path, n);
157 if (n && pathname[n - 1] != '/') {
158 pathname[n++] = '/';
159 }
160 strcpy(pathname + n, filename);
161 if (!access(pathname, X_OK)) {
162 return pathname;
163 }
164 }
165 return filename;
166 }
167
168 static void
guess_cols(void)169 guess_cols(void) {
170 struct winsize ws;
171 char *c;
172
173 options.align = DEFAULT_ALIGN;
174 c = getenv("COLUMNS");
175 if (c && *c) {
176 char *endptr;
177 int cols;
178 cols = strtol(c, &endptr, 0);
179 if (cols > 0 && !*endptr) {
180 options.align = cols * 5 / 8;
181 }
182 } else if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) {
183 options.align = ws.ws_col * 5 / 8;
184 } else if (ioctl(2, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) {
185 options.align = ws.ws_col * 5 / 8;
186 }
187 }
188
189 static int
compile_libname(const char * expr,const char * a_lib,int lib_re_p,struct filter_lib_matcher * matcher)190 compile_libname(const char *expr, const char *a_lib, int lib_re_p,
191 struct filter_lib_matcher *matcher)
192 {
193 if (strcmp(a_lib, "MAIN") == 0) {
194 filter_lib_matcher_main_init(matcher);
195 } else {
196 /* Add ^ and $ to the library expression as well. */
197 char lib[strlen(a_lib) + 3];
198 sprintf(lib, "^%s$", a_lib);
199
200 enum filter_lib_matcher_type type
201 = lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME;
202
203 regex_t lib_re;
204 int status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0);
205 if (status != 0) {
206 char buf[100];
207 regerror(status, &lib_re, buf, sizeof buf);
208 fprintf(stderr, "Couldn't compile '%s': %s.\n",
209 expr, buf);
210 return -1;
211 }
212 filter_lib_matcher_name_init(matcher, type, lib_re);
213 }
214 return 0;
215 }
216
217 static int
add_filter_rule(struct filter * filt,const char * expr,enum filter_rule_type type,const char * a_sym,int sym_re_p,const char * a_lib,int lib_re_p)218 add_filter_rule(struct filter *filt, const char *expr,
219 enum filter_rule_type type,
220 const char *a_sym, int sym_re_p,
221 const char *a_lib, int lib_re_p)
222 {
223 struct filter_rule *rule = malloc(sizeof(*rule));
224 struct filter_lib_matcher *matcher = malloc(sizeof(*matcher));
225
226 if (rule == NULL || matcher == NULL) {
227 fail:
228 free(rule);
229 free(matcher);
230 return -1;
231 }
232
233 regex_t symbol_re;
234 {
235 /* Add ^ to the start of expression and $ to the end, so that
236 * we match the whole symbol name. Let the user write the "*"
237 * explicitly if they wish. */
238 char sym[strlen(a_sym) + 3];
239 sprintf(sym, "^%s$", a_sym);
240 int status = (sym_re_p ? regcomp : globcomp)
241 (&symbol_re, sym, 0);
242 if (status != 0) {
243 char buf[100];
244 regerror(status, &symbol_re, buf, sizeof buf);
245 fprintf(stderr, "Couldn't compile '%s': %s.\n",
246 expr, buf);
247 goto fail;
248 }
249 }
250
251 if (compile_libname(expr, a_lib, lib_re_p, matcher) < 0) {
252 regfree(&symbol_re);
253 goto fail;
254 }
255
256 filter_rule_init(rule, type, matcher, symbol_re);
257 filter_add_rule(filt, rule);
258 return 0;
259 }
260
261 static int
grok_libname_pattern(char ** libnamep,char ** libendp)262 grok_libname_pattern(char **libnamep, char **libendp)
263 {
264 char *libname = *libnamep;
265 char *libend = *libendp;
266
267 if (libend[0] != '/')
268 return 0;
269
270 *libend-- = 0;
271 if (libname != libend && libname[0] == '/')
272 ++libname;
273 else
274 fprintf(stderr, "Unmatched '/' in library name.\n");
275
276 *libendp = libend;
277 *libnamep = libname;
278 return 1;
279 }
280
281 static int
parse_filter(struct filter * filt,char * expr,int operators)282 parse_filter(struct filter *filt, char *expr, int operators)
283 {
284 /* Filter is a chain of sym@lib rules separated by '-' or '+'.
285 * If the filter expression starts with '-', the missing
286 * initial rule is implicitly *@*. */
287
288 enum filter_rule_type type = FR_ADD;
289
290 while (*expr != 0) {
291 size_t s = strcspn(expr, &"-+@"[operators ? 0 : 2]);
292 char *symname = expr;
293 char *libname;
294 char *next = expr + s + 1;
295 enum filter_rule_type this_type = type;
296
297 if (expr[s] == 0) {
298 libname = "*";
299 expr = next - 1;
300
301 } else if (expr[s] == '-' || expr[s] == '+') {
302 type = expr[s] == '-' ? FR_SUBTRACT : FR_ADD;
303 expr[s] = 0;
304 libname = "*";
305 expr = next;
306
307 } else {
308 assert(expr[s] == '@');
309 expr[s] = 0;
310 s = strcspn(next, &"-+"[operators ? 0 : 2]);
311 if (s == 0) {
312 libname = "*";
313 expr = next;
314 } else if (next[s] == 0) {
315 expr = next + s;
316 libname = next;
317 } else {
318 assert(next[s] == '-' || next[s] == '+');
319 type = next[s] == '-' ? FR_SUBTRACT : FR_ADD;
320 next[s] = 0;
321 expr = next + s + 1;
322 libname = next;
323 }
324 }
325
326 assert(*libname != 0);
327 char *symend = symname + strlen(symname) - 1;
328 char *libend = libname + strlen(libname) - 1;
329 int sym_is_re = 0;
330 int lib_is_re = 0;
331
332 /*
333 * /xxx/@... and ...@/xxx/ means that xxx are regular
334 * expressions. They are globs otherwise.
335 *
336 * /xxx@yyy/ is the same as /xxx/@/yyy/
337 *
338 * @/xxx matches library path name
339 * @.xxx matches library relative path name
340 */
341 if (symname[0] == '/') {
342 if (symname != symend && symend[0] == '/') {
343 ++symname;
344 *symend-- = 0;
345 sym_is_re = 1;
346
347 } else {
348 sym_is_re = 1;
349 lib_is_re = 1;
350 ++symname;
351
352 /* /XXX@YYY/ is the same as
353 * /XXX/@/YYY/. */
354 if (libend[0] != '/')
355 fprintf(stderr, "Unmatched '/'"
356 " in symbol name.\n");
357 else
358 *libend-- = 0;
359 }
360 }
361
362 /* If libname ends in '/', then we expect '/' in the
363 * beginning too. Otherwise the initial '/' is part
364 * of absolute file name. */
365 if (!lib_is_re)
366 lib_is_re = grok_libname_pattern(&libname, &libend);
367
368 if (*symname == 0) /* /@AA/ */
369 symname = "*";
370 if (*libname == 0) /* /aa@/ */
371 libname = "*";
372
373 add_filter_rule(filt, expr, this_type,
374 symname, sym_is_re,
375 libname, lib_is_re);
376 }
377
378 return 0;
379 }
380
381 static struct filter *
recursive_parse_chain(const char * orig,char * expr,int operators)382 recursive_parse_chain(const char *orig, char *expr, int operators)
383 {
384 struct filter *filt = malloc(sizeof(*filt));
385 if (filt == NULL) {
386 fprintf(stderr, "(Part of) filter will be ignored: '%s': %s.\n",
387 expr, strerror(errno));
388 return NULL;
389 }
390
391 filter_init(filt);
392 if (parse_filter(filt, expr, operators) < 0) {
393 fprintf(stderr, "Filter '%s' will be ignored.\n", orig);
394 free(filt);
395 filt = NULL;
396 }
397
398 return filt;
399 }
400
401 static struct filter **
slist_chase_end(struct filter ** begin)402 slist_chase_end(struct filter **begin)
403 {
404 for (; *begin != NULL; begin = &(*begin)->next)
405 ;
406 return begin;
407 }
408
409 static void
parse_filter_chain(const char * expr,struct filter ** retp)410 parse_filter_chain(const char *expr, struct filter **retp)
411 {
412 char *str = strdup(expr);
413 if (str == NULL) {
414 fprintf(stderr, "Filter '%s' will be ignored: %s.\n",
415 expr, strerror(errno));
416 return;
417 }
418 /* Support initial '!' for backward compatibility. */
419 if (str[0] == '!')
420 str[0] = '-';
421
422 *slist_chase_end(retp) = recursive_parse_chain(expr, str, 1);
423 free(str);
424 }
425
426 static int
parse_int(const char * optarg,char opt,int min,int max)427 parse_int(const char *optarg, char opt, int min, int max)
428 {
429 char *endptr;
430 long int l = strtol(optarg, &endptr, 0);
431 if (l < min || (max != 0 && l > max)
432 || *optarg == 0 || *endptr != 0) {
433 const char *fmt = max != 0
434 ? "Invalid argument to -%c: '%s'. Use integer %d..%d.\n"
435 : "Invalid argument to -%c: '%s'. Use integer >=%d.\n";
436 fprintf(stderr, fmt, opt, optarg, min, max);
437 exit(1);
438 }
439 return (int)l;
440 }
441
442 int
parse_colon_separated_list(const char * paths,struct vect * vec)443 parse_colon_separated_list(const char *paths, struct vect *vec)
444 {
445 /* PATHS contains a colon-separated list of directories and
446 * files to load. It's modeled after shell PATH variable,
447 * which doesn't allow escapes. PYTHONPATH in CPython behaves
448 * the same way. So let's follow suit, it makes things easier
449 * to us. */
450
451 char *clone = strdup(paths);
452 if (clone == NULL) {
453 fprintf(stderr, "Couldn't parse argument %s: %s.\n",
454 paths, strerror(errno));
455 return -1;
456 }
457
458 /* It's undesirable to use strtok, because we want the string
459 * "a::b" to have three elements. */
460 char *tok = clone - 1;
461 char *end = clone + strlen(clone);
462 while (tok < end) {
463 ++tok;
464 size_t len = strcspn(tok, ":");
465 tok[len] = 0;
466
467 struct opt_F_t arg = {
468 .pathname = tok,
469 .own_pathname = tok == clone,
470 };
471 if (VECT_PUSHBACK(vec, &arg) < 0)
472 /* Presumably this is not a deal-breaker. */
473 fprintf(stderr, "Couldn't store component of %s: %s.\n",
474 paths, strerror(errno));
475
476 tok += len;
477 }
478
479 return 0;
480 }
481
482 void
opt_F_destroy(struct opt_F_t * entry)483 opt_F_destroy(struct opt_F_t *entry)
484 {
485 if (entry == NULL)
486 return;
487 if (entry->own_pathname)
488 free(entry->pathname);
489 }
490
491 enum opt_F_kind
opt_F_get_kind(struct opt_F_t * entry)492 opt_F_get_kind(struct opt_F_t *entry)
493 {
494 if (entry->kind == OPT_F_UNKNOWN) {
495 struct stat st;
496 if (lstat(entry->pathname, &st) < 0) {
497 fprintf(stderr, "Couldn't stat %s: %s\n",
498 entry->pathname, strerror(errno));
499 entry->kind = OPT_F_BROKEN;
500 } else if (S_ISDIR(st.st_mode)) {
501 entry->kind = OPT_F_DIR;
502 } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
503 entry->kind = OPT_F_FILE;
504 } else {
505 fprintf(stderr, "%s is neither a regular file, "
506 "nor a directory.\n", entry->pathname);
507 entry->kind = OPT_F_BROKEN;
508 }
509 }
510 assert(entry->kind != OPT_F_UNKNOWN);
511 return entry->kind;
512 }
513
514 char **
process_options(int argc,char ** argv)515 process_options(int argc, char **argv)
516 {
517 VECT_INIT(&opt_F, struct opt_F_t);
518
519 progname = argv[0];
520 options.output = stderr;
521 options.no_signals = 0;
522 #if defined(HAVE_UNWINDER)
523 options.bt_depth = -1;
524 #endif /* defined(HAVE_UNWINDER) */
525
526 guess_cols();
527
528 int libcalls = 1;
529
530 while (1) {
531 int c;
532 char *p;
533 #ifdef HAVE_GETOPT_LONG
534 int option_index = 0;
535 static struct option long_options[] = {
536 {"align", 1, 0, 'a'},
537 {"config", 1, 0, 'F'},
538 {"debug", 1, 0, 'D'},
539 # ifdef USE_DEMANGLE
540 {"demangle", 0, 0, 'C'},
541 # endif
542 {"indent", 1, 0, 'n'},
543 {"help", 0, 0, 'h'},
544 {"library", 1, 0, 'l'},
545 {"output", 1, 0, 'o'},
546 {"version", 0, 0, 'V'},
547 {"no-signals", 0, 0, 'b'},
548 # if defined(HAVE_UNWINDER)
549 {"where", 1, 0, 'w'},
550 # endif /* defined(HAVE_UNWINDER) */
551 {0, 0, 0, 0}
552 };
553 #endif
554
555 const char *opts = "+"
556 #ifdef USE_DEMANGLE
557 "C"
558 #endif
559 #if defined(HAVE_UNWINDER)
560 "w:"
561 #endif
562 "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:";
563
564 #ifdef HAVE_GETOPT_LONG
565 c = getopt_long(argc, argv, opts, long_options, &option_index);
566 #else
567 c = getopt(argc, argv, opts);
568 #endif
569 if (c == -1) {
570 break;
571 }
572 switch (c) {
573 case 'a':
574 options.align = parse_int(optarg, 'a', 0, 0);
575 break;
576 case 'A':
577 options.arraylen = parse_int(optarg, 'A', 0, 0);
578 break;
579 case 'b':
580 options.no_signals = 1;
581 break;
582 case 'c':
583 options.summary++;
584 break;
585 #ifdef USE_DEMANGLE
586 case 'C':
587 options.demangle++;
588 break;
589 #endif
590 case 'D':
591 if (optarg[0]=='h') {
592 usage_debug();
593 exit(0);
594 }
595 options.debug = strtoul(optarg,&p,8);
596 if (*p) {
597 fprintf(stderr, "%s: --debug requires an octal argument\n", progname);
598 err_usage();
599 }
600 break;
601
602 case 'e':
603 parse_filter_chain(optarg, &options.plt_filter);
604 break;
605
606 case 'f':
607 options.follow = 1;
608 break;
609 case 'F':
610 parse_colon_separated_list(optarg, &opt_F);
611 break;
612 case 'h':
613 usage();
614 exit(0);
615 case 'i':
616 opt_i++;
617 break;
618
619 case 'l': {
620 size_t patlen = strlen(optarg);
621 char buf[patlen + 2];
622 sprintf(buf, "@%s", optarg);
623 *slist_chase_end(&options.export_filter)
624 = recursive_parse_chain(buf, buf, 0);
625 break;
626 }
627
628 case 'L':
629 libcalls = 0;
630 break;
631 case 'n':
632 options.indent = parse_int(optarg, 'n', 0, 20);
633 break;
634 case 'o':
635 options.output = fopen(optarg, "w");
636 if (!options.output) {
637 fprintf(stderr,
638 "can't open %s for writing: %s\n",
639 optarg, strerror(errno));
640 exit(1);
641 }
642 setvbuf(options.output, (char *)NULL, _IOLBF, 0);
643 fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC);
644 break;
645 case 'p':
646 {
647 struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t));
648 if (!tmp) {
649 perror("ltrace: malloc");
650 exit(1);
651 }
652 tmp->pid = parse_int(optarg, 'p', 1, 0);
653 tmp->next = opt_p;
654 opt_p = tmp;
655 break;
656 }
657 case 'r':
658 opt_r++;
659 break;
660 case 's':
661 options.strlen = parse_int(optarg, 's', 0, 0);
662 break;
663 case 'S':
664 options.syscalls = 1;
665 break;
666 case 't':
667 opt_t++;
668 break;
669 case 'T':
670 opt_T++;
671 break;
672 case 'u':
673 options.user = optarg;
674 break;
675 case 'V':
676 printf("ltrace " PACKAGE_VERSION "\n"
677 "Copyright (C) 2010-2013 Petr Machata, Red Hat Inc.\n"
678 "Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.\n"
679 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
680 "This is free software: you are free to change and redistribute it.\n"
681 "There is NO WARRANTY, to the extent permitted by law.\n");
682 exit(0);
683 break;
684 #if defined(HAVE_UNWINDER)
685 case 'w':
686 options.bt_depth = parse_int(optarg, 'w', 1, 0);
687 break;
688 #endif /* defined(HAVE_UNWINDER) */
689
690 case 'x':
691 parse_filter_chain(optarg, &options.static_filter);
692 break;
693
694 default:
695 err_usage();
696 }
697 }
698 argc -= optind;
699 argv += optind;
700
701 /* If neither -e, nor -l, nor -L are used, set default -e.
702 * Use @MAIN for now, as that's what ltrace used to have in
703 * the past. XXX Maybe we should make this "*" instead. */
704 if (libcalls
705 && options.plt_filter == NULL
706 && options.export_filter == NULL) {
707 parse_filter_chain("@MAIN", &options.plt_filter);
708 options.hide_caller = 1;
709 }
710 if (!libcalls && options.plt_filter != NULL) {
711 fprintf(stderr,
712 "%s: Option -L can't be used with -e or -l.\n",
713 progname);
714 err_usage();
715 }
716
717 if (!opt_p && argc < 1) {
718 fprintf(stderr, "%s: too few arguments\n", progname);
719 err_usage();
720 }
721 if (opt_r && opt_t) {
722 fprintf(stderr,
723 "%s: Options -r and -t can't be used together\n",
724 progname);
725 err_usage();
726 }
727 if (argc > 0) {
728 command = search_for_command(argv[0]);
729 }
730 return &argv[0];
731 }
732