1This file is type.def, from which is created type.c. 2It implements the builtin "type" in Bash. 3 4Copyright (C) 1987-2020 Free Software Foundation, Inc. 5 6This file is part of GNU Bash, the Bourne Again SHell. 7 8Bash is free software: you can redistribute it and/or modify 9it under the terms of the GNU General Public License as published by 10the Free Software Foundation, either version 3 of the License, or 11(at your option) any later version. 12 13Bash is distributed in the hope that it will be useful, 14but WITHOUT ANY WARRANTY; without even the implied warranty of 15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16GNU General Public License for more details. 17 18You should have received a copy of the GNU General Public License 19along with Bash. If not, see <http://www.gnu.org/licenses/>. 20 21$PRODUCES type.c 22 23$BUILTIN type 24$FUNCTION type_builtin 25$SHORT_DOC type [-afptP] name [name ...] 26Display information about command type. 27 28For each NAME, indicate how it would be interpreted if used as a 29command name. 30 31Options: 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 45 46Arguments: 47 NAME Command name to be interpreted. 48 49Exit Status: 50Returns success if all of the NAMEs are found; fails if any are not found. 51$END 52 53#include <config.h> 54 55#include "../bashtypes.h" 56#include "posixstat.h" 57 58#if defined (HAVE_UNISTD_H) 59# include <unistd.h> 60#endif 61 62#include <stdio.h> 63#include "../bashansi.h" 64#include "../bashintl.h" 65 66#include "../shell.h" 67#include "../parser.h" 68#include "../execute_cmd.h" 69#include "../findcmd.h" 70#include "../hashcmd.h" 71 72#if defined (ALIAS) 73#include "../alias.h" 74#endif /* ALIAS */ 75 76#include "common.h" 77#include "bashgetopt.h" 78 79extern int find_reserved_word PARAMS((char *)); 80 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'. 88 89 -p Returns the pathname of the file if -type is 90 a file. 91 92 -a Returns all occurrences of words, whether they 93 be a filename in the path, alias, function, 94 or builtin. 95 96 -f Suppress shell function lookup, like `command'. 97 98 -P Force a path search even in the presence of other 99 definitions. 100 101 Order of evaluation: 102 alias 103 keyword 104 function 105 builtin 106 file 107 */ 108 109int 110type_builtin (list) 111 WORD_LIST *list; 112{ 113 int dflags, any_failed, opt; 114 WORD_LIST *this; 115 116 if (list == 0) 117 return (EXECUTION_SUCCESS); 118 119 dflags = CDESC_SHORTDESC; /* default */ 120 any_failed = 0; 121 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) 127 { 128 char *flag = &(this->word->word[1]); 129 130 if (STREQ (flag, "type") || STREQ (flag, "-type")) 131 { 132 this->word->word[1] = 't'; 133 this->word->word[2] = '\0'; 134 } 135 else if (STREQ (flag, "path") || STREQ (flag, "-path")) 136 { 137 this->word->word[1] = 'p'; 138 this->word->word[2] = '\0'; 139 } 140 else if (STREQ (flag, "all") || STREQ (flag, "-all")) 141 { 142 this->word->word[1] = 'a'; 143 this->word->word[2] = '\0'; 144 } 145 } 146 147 reset_internal_getopt (); 148 while ((opt = internal_getopt (list, "afptP")) != -1) 149 { 150 switch (opt) 151 { 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); 174 } 175 } 176 list = loptend; 177 178 while (list) 179 { 180 int found; 181 182 found = describe_command (list->word->word, dflags); 183 184 if (!found && (dflags & (CDESC_PATH_ONLY|CDESC_TYPE)) == 0) 185 sh_notfound (list->word->word); 186 187 any_failed += found == 0; 188 list = list->next; 189 } 190 191 opt = (any_failed == 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE; 192 return (sh_chkwrite (opt)); 193} 194 195/* 196 * Describe COMMAND as required by the type and command builtins. 197 * 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 208 * 209 * CDESC_ALL says whether or not to look for all occurrences of COMMAND, or 210 * return after finding it once. 211 */ 212int 213describe_command (command, dflags) 214 char *command; 215 int dflags; 216{ 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 223 224 all = (dflags & CDESC_ALL) != 0; 225 found = found_file = 0; 226 full_path = (char *)NULL; 227 228#if defined (ALIAS) 229 /* Command is an alias? */ 230 if (((dflags & CDESC_FORCE_PATH) == 0) && expand_aliases && (alias = find_alias (command))) 231 { 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) 237 { 238 x = sh_single_quote (alias->value); 239 printf ("alias %s=%s\n", command, x); 240 free (x); 241 } 242 243 found = 1; 244 245 if (all == 0) 246 return (1); 247 } 248#endif /* ALIAS */ 249 250 /* Command is a shell reserved word? */ 251 if (((dflags & CDESC_FORCE_PATH) == 0) && (i = find_reserved_word (command)) >= 0) 252 { 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); 259 260 found = 1; 261 262 if (all == 0) 263 return (1); 264 } 265 266 /* Command is a function? */ 267 if (((dflags & (CDESC_FORCE_PATH|CDESC_NOFUNCS)) == 0) && (func = find_function (command))) 268 { 269 if (dflags & CDESC_TYPE) 270 puts ("function"); 271 else if (dflags & CDESC_SHORTDESC) 272 { 273 char *result; 274 275 printf (_("%s is a function\n"), command); 276 277 /* We're blowing away THE_PRINTED_COMMAND here... */ 278 279 result = named_function_string (command, function_cell (func), FUNC_MULTILINE|FUNC_EXTERNAL); 280 printf ("%s\n", result); 281 } 282 else if (dflags & CDESC_REUSABLE) 283 printf ("%s\n", command); 284 285 found = 1; 286 287 if (all == 0) 288 return (1); 289 } 290 291 /* Command is a builtin? */ 292 if (((dflags & CDESC_FORCE_PATH) == 0) && find_shell_builtin (command)) 293 { 294 if (dflags & CDESC_TYPE) 295 puts ("builtin"); 296 else if (dflags & CDESC_SHORTDESC) 297 { 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); 302 } 303 else if (dflags & CDESC_REUSABLE) 304 printf ("%s\n", command); 305 306 found = 1; 307 308 if (all == 0) 309 return (1); 310 } 311 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)) 316 { 317 f = file_status (command); 318 if (f & FS_EXECABLE) 319 { 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); 326 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); 331 } 332 } 333 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)) 337 { 338 if (full_path = phash_search (command)) 339 { 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); 346 347 free (full_path); 348 return (1); 349 } 350 } 351 352 /* Now search through $PATH. */ 353 while (1) 354 { 355 if (dflags & CDESC_STDPATH) /* command -p, all cannot be non-zero */ 356 { 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 */ 361 } 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? */ 366 367 if (full_path == 0) 368 break; 369 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) 375 { 376 f = file_status (full_path); 377 if ((f & FS_EXECABLE) == 0) 378 { 379 free (full_path); 380 full_path = (char *)NULL; 381 if (all == 0) 382 break; 383 } 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)) 387 { 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; 392 } 393 } 394 /* If we require a full path and don't have one, make one */ 395 else if ((dflags & CDESC_ABSPATH) && ABSPATH (full_path) == 0) 396 { 397 x = sh_makepath ((char *)NULL, full_path, MP_DOCWD|MP_RMDOT); 398 free (full_path); 399 full_path = x; 400 } 401 402 found_file++; 403 found = 1; 404 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); 411 412 free (full_path); 413 full_path = (char *)NULL; 414 415 if (all == 0) 416 break; 417 } 418 419 return (found); 420} 421