static void parse_filter_chain(const char *expr, struct filter **retp) { char *str = strdup(expr); if (str == NULL) { fprintf(stderr, "Filter '%s' will be ignored: %s.\n", expr, strerror(errno)); return; } /* Support initial '!' for backward compatibility. */ if (str[0] == '!') str[0] = '-'; *slist_chase_end(retp) = recursive_parse_chain(str, 1); free(str); }
int parse_colon_separated_list(const char *paths, struct vect *vec) { /* PATHS contains a colon-separated list of directories and * files to load. It's modeled after shell PATH variable, * which doesn't allow escapes. PYTHONPATH in CPython behaves * the same way. So let's follow suit, it makes things easier * to us. */ char *clone = strdup(paths); if (clone == NULL) { fprintf(stderr, "Couldn't parse argument %s: %s.\n", paths, strerror(errno)); return -1; } /* It's undesirable to use strtok, because we want the string * "a::b" to have three elements. */ char *tok = clone - 1; char *end = clone + strlen(clone); while (tok < end) { ++tok; size_t len = strcspn(tok, ":"); tok[len] = 0; struct opt_F_t arg = { .pathname = tok, .own_pathname = tok == clone, }; if (VECT_PUSHBACK(vec, &arg) < 0) /* Presumably this is not a deal-breaker. */ fprintf(stderr, "Couldn't store component of %s: %s.\n", paths, strerror(errno)); tok += len; } return 0; } void opt_F_destroy(struct opt_F_t *entry) { if (entry == NULL) return; if (entry->own_pathname) free(entry->pathname); } enum opt_F_kind opt_F_get_kind(struct opt_F_t *entry) { if (entry->kind == OPT_F_UNKNOWN) { struct stat st; if (lstat(entry->pathname, &st) < 0) { fprintf(stderr, "Couldn't stat %s: %s\n", entry->pathname, strerror(errno)); entry->kind = OPT_F_BROKEN; } else if (S_ISDIR(st.st_mode)) { entry->kind = OPT_F_DIR; } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { entry->kind = OPT_F_FILE; } else { fprintf(stderr, "%s is neither a regular file, " "nor a directory.\n", entry->pathname); entry->kind = OPT_F_BROKEN; } } assert(entry->kind != OPT_F_UNKNOWN); return entry->kind; } char ** process_options(int argc, char **argv) { VECT_INIT(&opt_F, struct opt_F_t); progname = argv[0]; options.output = stderr; options.no_signals = 0; #if defined(HAVE_UNWINDER) options.bt_depth = -1; #endif /* defined(HAVE_UNWINDER) */ guess_cols(); int libcalls = 1; while (1) { int c; char *p; #ifdef HAVE_GETOPT_LONG int option_index = 0; static struct option long_options[] = { {"align", 1, 0, 'a'}, {"config", 1, 0, 'F'}, {"debug", 1, 0, 'D'}, # ifdef USE_DEMANGLE {"demangle", 0, 0, 'C'}, # endif {"indent", 1, 0, 'n'}, {"help", 0, 0, 'h'}, {"library", 1, 0, 'l'}, {"output", 1, 0, 'o'}, {"version", 0, 0, 'V'}, {"no-signals", 0, 0, 'b'}, # if defined(HAVE_UNWINDER) {"where", 1, 0, 'w'}, # endif /* defined(HAVE_UNWINDER) */ {0, 0, 0, 0} }; #endif const char *opts = "+" #ifdef USE_DEMANGLE "C" #endif #if defined(HAVE_UNWINDER) "w:" #endif "cfhiLrStTVba:A:D:e:F:l:n:o:p:s:u:x:"; #ifdef HAVE_GETOPT_LONG c = getopt_long(argc, argv, opts, long_options, &option_index); #else c = getopt(argc, argv, opts); #endif if (c == -1) { break; } switch (c) { case 'a': options.align = parse_int(optarg, 'a', 0, 0); break; case 'A': options.arraylen = parse_int(optarg, 'A', 0, 0); break; case 'b': options.no_signals = 1; break; case 'c': options.summary++; break; #ifdef USE_DEMANGLE case 'C': options.demangle++; break; #endif case 'D': if (optarg[0]=='h') { usage_debug(); exit(0); } options.debug = strtoul(optarg,&p,8); if (*p) { fprintf(stderr, "%s: --debug requires an octal argument\n", progname); err_usage(); } break; case 'e': parse_filter_chain(optarg, &options.plt_filter); break; case 'f': options.follow = 1; break; case 'F': parse_colon_separated_list(optarg, &opt_F); break; case 'h': usage(); exit(0); case 'i': opt_i++; break; 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, buf, 0); break; } case 'L': libcalls = 0; break; case 'n': options.indent = parse_int(optarg, 'n', 0, 20); break; case 'o': options.output = fopen(optarg, "w"); if (!options.output) { fprintf(stderr, "can't open %s for writing: %s\n", optarg, strerror(errno)); exit(1); } setvbuf(options.output, (char *)NULL, _IOLBF, 0); fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC); break; case 'p': { struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t)); if (!tmp) { perror("ltrace: malloc"); exit(1); } tmp->pid = parse_int(optarg, 'p', 1, 0); tmp->next = opt_p; opt_p = tmp; break; } case 'r': opt_r++; break; case 's': options.strlen = parse_int(optarg, 's', 0, 0); break; case 'S': options.syscalls = 1; break; case 't': opt_t++; break; case 'T': opt_T++; break; case 'u': options.user = optarg; break; case 'V': printf("ltrace " PACKAGE_VERSION "\n" "Copyright (C) 2010-2013 Petr Machata, Red Hat Inc.\n" "Copyright (C) 1997-2009 Juan Cespedes <*****@*****.**>.\n" "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n"); exit(0); break; #if defined(HAVE_UNWINDER) case 'w': options.bt_depth = parse_int(optarg, 'w', 1, 0); break; #endif /* defined(HAVE_UNWINDER) */ case 'x': parse_filter_chain(optarg, &options.static_filter); break; default: err_usage(); } } argc -= optind; argv += optind; /* 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); err_usage(); } if (opt_r && opt_t) { fprintf(stderr, "%s: Options -r and -t can't be used together\n", progname); err_usage(); } if (argc > 0) { command = search_for_command(argv[0]); } return &argv[0]; }