46 using std::stringstream;
49 int processOpts(
int argc,
char *argv[]);
52 void split(
const std::string &s,
char delim, vector<string> &elems);
53 int findPaths(
string filename);
54 void readAliasesAndFunctions();
70 : all(0), readAliases(0), readFunctions(0), showDot(0), skipDot(0),
71 showTilde(0), skipTilde(0){};
81 static list<string> aliases, functions;
82 static string home, cwd;
84 int processOpts(
int argc,
char *argv[])
86 int c, longOption, ttyOnly = 0;
87 struct option long_options[] = {
88 {
"all", no_argument, &opts.all, 1},
89 {
"read-alias", no_argument, &opts.readAliases, 1},
90 {
"skip-alias", no_argument, &opts.readAliases, 0},
91 {
"read-functions", no_argument, &opts.readFunctions, 1},
92 {
"skip-functions", no_argument, &opts.readFunctions, 0},
93 {
"skip-dot", no_argument, &longOption, opt_skip_dot},
94 {
"show-dot", no_argument, &longOption, opt_show_dot},
95 {
"skip-tilde", no_argument, &longOption, opt_skip_tilde},
96 {
"show-tilde", no_argument, &longOption, opt_show_tilde},
97 {
"tty-only", no_argument, &longOption, opt_tty_only},
98 {
"version", no_argument, 0,
'v'},
99 {
"help", no_argument, 0,
'h'},
104 c = getopt_long(argc, argv,
"aivVh", long_options, NULL);
115 opts.skipDot = !ttyOnly;
118 opts.showDot = !ttyOnly;
121 opts.skipTilde = !ttyOnly;
124 opts.showTilde = !ttyOnly;
127 ttyOnly = !isatty(1);
135 opts.readAliases = 1;
152 cout <<
"Usage: which [options] [--] COMMAND [...]" << endl;
153 cout <<
"Write the full path of COMMAND(s) to standard output." << endl;
155 cout <<
" --version, -[vV] Print version and exit successfully." << endl;
156 cout <<
" --help, Print this help and exit successfully." << endl;
157 cout <<
" --skip-dot Skip directories in PATH that start with a dot." 159 cout <<
" --skip-tilde Skip directories in PATH that start with a " 162 cout <<
" --show-dot Don't expand a dot to current directory in " 165 cout <<
" --show-tilde Output a tilde for HOME directory for non-root." 167 cout <<
" --tty-only Stop processing options on the right if not on " 170 cout <<
" --all, -a Print all matches in PATH, not just the first" 172 cout <<
" --read-alias, -i Read list of aliases from stdin." << endl;
173 cout <<
" --skip-alias Ignore option --read-alias; don't read stdin." 175 cout <<
" --read-functions Read shell functions from stdin." << endl;
176 cout <<
" --skip-functions Ignore option --read-functions; don't read " 180 cout <<
"Recommended use is to write the output of (alias; declare -f) to " 183 cout <<
"input, so that which can show aliases and shell functions. See " 186 cout <<
"examples." << endl;
188 cout <<
"If the options --read-alias and/or --read-functions are specified " 191 cout <<
"output can be a full alias or function definition, optionally " 194 cout <<
"the full path of each command used inside of those." << endl;
199 cout <<
"which v1.0, Copyright (C) 2014 Nathan Hoad" << endl;
202 void split(
const std::string &s,
char delim, vector<string> &elems)
206 while (std::getline(ss, item, delim))
208 elems.push_back(item);
212 int findPaths(
string filename)
218 for (
auto &alias : aliases)
220 if (alias.find(filename) == 0)
222 cout << alias << endl;
228 for (
auto &
function : functions)
230 if (
function.find(filename) == 0)
232 cout <<
function << endl;
238 path = getenv(
"PATH");
240 split(path ? path :
"",
':', dirs);
242 for (
auto &dir : dirs)
245 while (dir.rbegin() != dir.rend() && *dir.rbegin() ==
'/')
248 string fullpath = dir +
"/" + filename;
252 if ((stat(fullpath.c_str(), &sb) == 0) && sb.st_mode & S_IXUSR)
254 if (opts.showDot && dir[0] ==
'.' &&
255 fullpath.compare(0, cwd.size(), cwd) == 0)
257 printed_path = string(
"./") + filename;
261 if (opts.showTilde && !home.empty() &&
262 fullpath.compare(0, home.size(), home) == 0)
264 fullpath = fullpath.substr(
265 home.size(), fullpath.size() - home.size());
266 printed_path = string(
"~") + fullpath;
270 printed_path = fullpath;
274 if (printed_path.size())
276 if (opts.skipTilde && printed_path[0] ==
'~' && geteuid() != 0)
278 else if (opts.skipDot && printed_path[0] ==
'.')
280 cout << printed_path << endl;
291 void readAliasesAndFunctions()
293 if (!opts.readFunctions && !opts.readAliases)
297 cerr <<
"which: warning: stdin is a tty" << endl;
299 string line,
function;
301 while (std::getline(cin, line,
'\n'))
303 if (opts.readAliases && regex_match(line, regex(
"^.+=.+?$")))
304 aliases.push_back(line);
305 else if (opts.readFunctions)
307 function = line +
"\n";
308 while (std::getline(cin, line,
'\n') && line !=
"}")
310 function += line +
"\n";
316 functions.push_back(
function);
322 cerr <<
"which: line doesn't appear to be an alias and functions " 335 _home = getenv(
"HOME");
336 home = _home ? _home :
"";
338 memset(_cwd, 0, PATH_MAX);
339 if (getcwd(_cwd, PATH_MAX) == NULL)
341 cerr <<
"which: unable to retrieve current working directory " 342 << strerror(errno) << endl;
349 int main(
int argc,
char *argv[])
353 if (processOpts(argc, argv) != 0)
357 readAliasesAndFunctions();
359 status = EXIT_SUCCESS;
361 for (
int i = optind; i < argc; i++)
363 if (findPaths(argv[i]) != 0)
365 cout << argv[i] <<
" not found" << endl;
366 status = EXIT_FAILURE;