improve of cmpl.
[bush.git] / builtins / type.def
blob1f333eb0ca042ecf29f10b54f7260ce67defb3cb
1 This file is type.def, from which is created type.c.
2 It implements the builtin "type" in Bush.
4 Copyright (C) 1987-2020 Free Software Foundation, Inc.
6 This file is part of GNU Bush, the Bourne Again SHell.
8 Bush is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 Bush is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Bush. If not, see <http://www.gnu.org/licenses/>.
21 $PRODUCES type.c
23 $BUILTIN type
24 $FUNCTION type_builtin
25 $SHORT_DOC type [-afptP] name [name ...]
26 Display information about command type.
28 For each NAME, indicate how it would be interpreted if used as a
29 command name.
31 Options:
32 -a display all locations containing an executable named NAME;
33 includes aliases, builtins, and functions, if and only if
34 the `-p' option is not also used
35 -f suppress shell function lookup
36 -P force a PATH search for each NAME, even if it is an alias,
37 builtin, or function, and returns the name of the disk file
38 that would be executed
39 -p returns either the name of the disk file that would be executed,
40 or nothing if `type -t NAME' would not return `file'
41 -t output a single word which is one of `alias', `keyword',
42 `function', `builtin', `file' or `', if NAME is an alias,
43 shell reserved word, shell function, shell builtin, disk file,
44 or not found, respectively
46 Arguments:
47 NAME Command name to be interpreted.
49 Exit Status:
50 Returns success if all of the NAMEs are found; fails if any are not found.
51 $END
53 #include <config.h>
55 #include "../src/bushtypes.h"
56 #include "posixstat.h"
58 #if defined (HAVE_UNISTD_H)
59 # include <unistd.h>
60 #endif
62 #include <stdio.h>
63 #include "../src/bushansi.h"
64 #include "../src/bushintl.h"
66 #include "../src/shell.h"
67 #include "../src/lxrgmr/parser.h"
68 #include "../src/runner/execute_cmd.h"
69 #include "../src/impl/findcmd.h"
70 #include "../src/hashcmd.h"
72 #if defined (ALIAS)
73 #include "../src/impl/alias.h"
74 #endif /* ALIAS */
76 #include "common.h"
77 #include "bushgetopt.h"
79 extern int find_reserved_word PARAMS((char *));
81 /* For each word in LIST, find out what the shell is going to do with
82 it as a simple command. i.e., which file would this shell use to
83 execve, or if it is a builtin command, or an alias. Possible flag
84 arguments:
85 -t Returns the "type" of the object, one of
86 `alias', `keyword', `function', `builtin',
87 or `file'.
89 -p Returns the pathname of the file if -type is
90 a file.
92 -a Returns all occurrences of words, whether they
93 be a filename in the path, alias, function,
94 or builtin.
96 -f Suppress shell function lookup, like `command'.
98 -P Force a path search even in the presence of other
99 definitions.
101 Order of evaluation:
102 alias
103 keyword
104 function
105 builtin
106 file
110 type_builtin (list)
111 WORD_LIST *list;
113 int dflags, any_failed, opt;
114 WORD_LIST *this;
116 if (list == 0)
117 return (EXECUTION_SUCCESS);
119 dflags = CDESC_SHORTDESC; /* default */
120 any_failed = 0;
122 /* Handle the obsolescent `-type', `-path', and `-all' by prescanning
123 the arguments and converting those options to the form that
124 internal_getopt recognizes. Converts `--type', `--path', and `--all'
125 also. THIS SHOULD REALLY GO AWAY. */
126 for (this = list; this && this->word->word[0] == '-'; this = this->next)
128 char *flag = &(this->word->word[1]);
130 if (STREQ (flag, "type") || STREQ (flag, "-type"))
132 this->word->word[1] = 't';
133 this->word->word[2] = '\0';
135 else if (STREQ (flag, "path") || STREQ (flag, "-path"))
137 this->word->word[1] = 'p';
138 this->word->word[2] = '\0';
140 else if (STREQ (flag, "all") || STREQ (flag, "-all"))
142 this->word->word[1] = 'a';
143 this->word->word[2] = '\0';
147 reset_internal_getopt ();
148 while ((opt = internal_getopt (list, "afptP")) != -1)
150 switch (opt)
152 case 'a':
153 dflags |= CDESC_ALL;
154 break;
155 case 'f':
156 dflags |= CDESC_NOFUNCS;
157 break;
158 case 'p':
159 dflags |= CDESC_PATH_ONLY;
160 dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
161 break;
162 case 't':
163 dflags |= CDESC_TYPE;
164 dflags &= ~(CDESC_PATH_ONLY|CDESC_SHORTDESC);
165 break;
166 case 'P': /* shorthand for type -ap */
167 dflags |= (CDESC_PATH_ONLY|CDESC_FORCE_PATH);
168 dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
169 break;
170 CASE_HELPOPT;
171 default:
172 builtin_usage ();
173 return (EX_USAGE);
176 list = loptend;
178 while (list)
180 int found;
182 found = describe_command (list->word->word, dflags);
184 if (!found && (dflags & (CDESC_PATH_ONLY|CDESC_TYPE)) == 0)
185 sh_notfound (list->word->word);
187 any_failed += found == 0;
188 list = list->next;
191 opt = (any_failed == 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
192 return (sh_chkwrite (opt));
196 * Describe COMMAND as required by the type and command builtins.
198 * Behavior is controlled by DFLAGS. Flag values are
199 * CDESC_ALL print all descriptions of a command
200 * CDESC_SHORTDESC print the description for type and command -V
201 * CDESC_REUSABLE print in a format that may be reused as input
202 * CDESC_TYPE print the type for type -t
203 * CDESC_PATH_ONLY print the path for type -p
204 * CDESC_FORCE_PATH force a path search for type -P
205 * CDESC_NOFUNCS skip function lookup for type -f
206 * CDESC_ABSPATH convert to absolute path, no ./ prefix
207 * CDESC_STDPATH command -p standard path list
209 * CDESC_ALL says whether or not to look for all occurrences of COMMAND, or
210 * return after finding it once.
213 describe_command (command, dflags)
214 char *command;
215 int dflags;
217 int found, i, found_file, f, all;
218 char *full_path, *x, *pathlist;
219 SHELL_VAR *func;
220 #if defined (ALIAS)
221 alias_t *alias;
222 #endif
224 all = (dflags & CDESC_ALL) != 0;
225 found = found_file = 0;
226 full_path = (char *)NULL;
228 #if defined (ALIAS)
229 /* Command is an alias? */
230 if (((dflags & CDESC_FORCE_PATH) == 0) && expand_aliases && (alias = find_alias (command)))
232 if (dflags & CDESC_TYPE)
233 puts ("alias");
234 else if (dflags & CDESC_SHORTDESC)
235 printf (_("%s is aliased to `%s'\n"), command, alias->value);
236 else if (dflags & CDESC_REUSABLE)
238 x = sh_single_quote (alias->value);
239 printf ("alias %s=%s\n", command, x);
240 free (x);
243 found = 1;
245 if (all == 0)
246 return (1);
248 #endif /* ALIAS */
250 /* Command is a shell reserved word? */
251 if (((dflags & CDESC_FORCE_PATH) == 0) && (i = find_reserved_word (command)) >= 0)
253 if (dflags & CDESC_TYPE)
254 puts ("keyword");
255 else if (dflags & CDESC_SHORTDESC)
256 printf (_("%s is a shell keyword\n"), command);
257 else if (dflags & CDESC_REUSABLE)
258 printf ("%s\n", command);
260 found = 1;
262 if (all == 0)
263 return (1);
266 /* Command is a function? */
267 if (((dflags & (CDESC_FORCE_PATH|CDESC_NOFUNCS)) == 0) && (func = find_function (command)))
269 if (dflags & CDESC_TYPE)
270 puts ("function");
271 else if (dflags & CDESC_SHORTDESC)
273 char *result;
275 printf (_("%s is a function\n"), command);
277 /* We're blowing away THE_PRINTED_COMMAND here... */
279 result = named_function_string (command, function_cell (func), FUNC_MULTILINE|FUNC_EXTERNAL);
280 printf ("%s\n", result);
282 else if (dflags & CDESC_REUSABLE)
283 printf ("%s\n", command);
285 found = 1;
287 if (all == 0)
288 return (1);
291 /* Command is a builtin? */
292 if (((dflags & CDESC_FORCE_PATH) == 0) && find_shell_builtin (command))
294 if (dflags & CDESC_TYPE)
295 puts ("builtin");
296 else if (dflags & CDESC_SHORTDESC)
298 if (posixly_correct && find_special_builtin (command) != 0)
299 printf (_("%s is a special shell builtin\n"), command);
300 else
301 printf (_("%s is a shell builtin\n"), command);
303 else if (dflags & CDESC_REUSABLE)
304 printf ("%s\n", command);
306 found = 1;
308 if (all == 0)
309 return (1);
312 /* Command is a disk file? */
313 /* If the command name given is already an absolute command, just
314 check to see if it is executable. */
315 if (absolute_program (command))
317 f = file_status (command);
318 if (f & FS_EXECABLE)
320 if (dflags & CDESC_TYPE)
321 puts ("file");
322 else if (dflags & CDESC_SHORTDESC)
323 printf (_("%s is %s\n"), command, command);
324 else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
325 printf ("%s\n", command);
327 /* There's no use looking in the hash table or in $PATH,
328 because they're not consulted when an absolute program
329 name is supplied. */
330 return (1);
334 /* If the user isn't doing "-a", then we might care about
335 whether the file is present in our hash table. */
336 if (all == 0 || (dflags & CDESC_FORCE_PATH))
338 if (full_path = phash_search (command))
340 if (dflags & CDESC_TYPE)
341 puts ("file");
342 else if (dflags & CDESC_SHORTDESC)
343 printf (_("%s is hashed (%s)\n"), command, full_path);
344 else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
345 printf ("%s\n", full_path);
347 free (full_path);
348 return (1);
352 /* Now search through $PATH. */
353 while (1)
355 if (dflags & CDESC_STDPATH) /* command -p, all cannot be non-zero */
357 pathlist = conf_standard_path ();
358 full_path = find_in_path (command, pathlist, FS_EXEC_PREFERRED|FS_NODIRS);
359 free (pathlist);
360 /* Will only go through this once, since all == 0 if STDPATH set */
362 else if (all == 0)
363 full_path = find_user_command (command);
364 else
365 full_path = user_command_matches (command, FS_EXEC_ONLY, found_file); /* XXX - should that be FS_EXEC_PREFERRED? */
367 if (full_path == 0)
368 break;
370 /* If we found the command as itself by looking through $PATH, it
371 probably doesn't exist. Check whether or not the command is an
372 executable file. If it's not, don't report a match. This is
373 the default posix mode behavior */
374 if (STREQ (full_path, command) || posixly_correct)
376 f = file_status (full_path);
377 if ((f & FS_EXECABLE) == 0)
379 free (full_path);
380 full_path = (char *)NULL;
381 if (all == 0)
382 break;
384 else if (ABSPATH (full_path))
385 ; /* placeholder; don't need to do anything yet */
386 else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY|CDESC_SHORTDESC))
388 f = MP_DOCWD | ((dflags & CDESC_ABSPATH) ? MP_RMDOT : 0);
389 x = sh_makepath ((char *)NULL, full_path, f);
390 free (full_path);
391 full_path = x;
394 /* If we require a full path and don't have one, make one */
395 else if ((dflags & CDESC_ABSPATH) && ABSPATH (full_path) == 0)
397 x = sh_makepath ((char *)NULL, full_path, MP_DOCWD|MP_RMDOT);
398 free (full_path);
399 full_path = x;
402 found_file++;
403 found = 1;
405 if (dflags & CDESC_TYPE)
406 puts ("file");
407 else if (dflags & CDESC_SHORTDESC)
408 printf (_("%s is %s\n"), command, full_path);
409 else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
410 printf ("%s\n", full_path);
412 free (full_path);
413 full_path = (char *)NULL;
415 if (all == 0)
416 break;
419 return (found);