1This file is type.def, from which is created type.c. 2It implements the builtin "type" in Bash. 3 4Copyright (C) 1987-2002 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 it under 9the terms of the GNU General Public License as published by the Free 10Software Foundation; either version 2, or (at your option) any later 11version. 12 13Bash is distributed in the hope that it will be useful, but WITHOUT ANY 14WARRANTY; without even the implied warranty of MERCHANTABILITY or 15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16for more details. 17 18You should have received a copy of the GNU General Public License along 19with Bash; see the file COPYING. If not, write to the Free Software 20Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. 21 22$PRODUCES type.c 23 24$BUILTIN type 25$FUNCTION type_builtin 26$SHORT_DOC type [-afptP] name [name ...] 27For each NAME, indicate how it would be interpreted if used as a 28command name. 29 30If the -t option is used, `type' outputs a single word which is one of 31`alias', `keyword', `function', `builtin', `file' or `', if NAME is an 32alias, shell reserved word, shell function, shell builtin, disk file, 33or unfound, respectively. 34 35If the -p flag is used, `type' either returns the name of the disk 36file that would be executed, or nothing if `type -t NAME' would not 37return `file'. 38 39If the -a flag is used, `type' displays all of the places that contain 40an executable named `file'. This includes aliases, builtins, and 41functions, if and only if the -p flag is not also used. 42 43The -f flag suppresses shell function lookup. 44 45The -P flag forces a PATH search for each NAME, even if it is an alias, 46builtin, or function, and returns the name of the disk file that would 47be executed. 48$END 49 50#include <config.h> 51 52#include "../bashtypes.h" 53#include "posixstat.h" 54 55#if defined (HAVE_UNISTD_H) 56# include <unistd.h> 57#endif 58 59#include <stdio.h> 60#include "../bashansi.h" 61#include "../bashintl.h" 62 63#include "../shell.h" 64#include "../findcmd.h" 65#include "../hashcmd.h" 66 67#if defined (ALIAS) 68#include "../alias.h" 69#endif /* ALIAS */ 70 71#include "common.h" 72#include "bashgetopt.h" 73 74extern int find_reserved_word __P((char *)); 75 76extern char *this_command_name; 77extern int expand_aliases, posixly_correct; 78 79/* For each word in LIST, find out what the shell is going to do with 80 it as a simple command. i.e., which file would this shell use to 81 execve, or if it is a builtin command, or an alias. Possible flag 82 arguments: 83 -t Returns the "type" of the object, one of 84 `alias', `keyword', `function', `builtin', 85 or `file'. 86 87 -p Returns the pathname of the file if -type is 88 a file. 89 90 -a Returns all occurrences of words, whether they 91 be a filename in the path, alias, function, 92 or builtin. 93 94 -f Suppress shell function lookup, like `command'. 95 96 -P Force a path search even in the presence of other 97 definitions. 98 99 Order of evaluation: 100 alias 101 keyword 102 function 103 builtin 104 file 105 */ 106 107int 108type_builtin (list) 109 WORD_LIST *list; 110{ 111 int dflags, successful_finds, opt; 112 WORD_LIST *this; 113 114 if (list == 0) 115 return (EXECUTION_SUCCESS); 116 117 dflags = CDESC_SHORTDESC; /* default */ 118 successful_finds = 0; 119 120 /* Handle the obsolescent `-type', `-path', and `-all' by prescanning 121 the arguments and converting those options to the form that 122 internal_getopt recognizes. Converts `--type', `--path', and `--all' 123 also. THIS SHOULD REALLY GO AWAY. */ 124 for (this = list; this && this->word->word[0] == '-'; this = this->next) 125 { 126 char *flag = &(this->word->word[1]); 127 128 if (STREQ (flag, "type") || STREQ (flag, "-type")) 129 { 130 this->word->word[1] = 't'; 131 this->word->word[2] = '\0'; 132 } 133 else if (STREQ (flag, "path") || STREQ (flag, "-path")) 134 { 135 this->word->word[1] = 'p'; 136 this->word->word[2] = '\0'; 137 } 138 else if (STREQ (flag, "all") || STREQ (flag, "-all")) 139 { 140 this->word->word[1] = 'a'; 141 this->word->word[2] = '\0'; 142 } 143 } 144 145 reset_internal_getopt (); 146 while ((opt = internal_getopt (list, "afptP")) != -1) 147 { 148 switch (opt) 149 { 150 case 'a': 151 dflags |= CDESC_ALL; 152 break; 153 case 'f': 154 dflags |= CDESC_NOFUNCS; 155 break; 156 case 'p': 157 dflags |= CDESC_PATH_ONLY; 158 dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC); 159 break; 160 case 't': 161 dflags |= CDESC_TYPE; 162 dflags &= ~(CDESC_PATH_ONLY|CDESC_SHORTDESC); 163 break; 164 case 'P': /* shorthand for type -ap */ 165 dflags |= (CDESC_PATH_ONLY|CDESC_FORCE_PATH); 166 dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC); 167 break; 168 default: 169 builtin_usage (); 170 return (EX_USAGE); 171 } 172 } 173 list = loptend; 174 175 while (list) 176 { 177 int found; 178 179 found = describe_command (list->word->word, dflags); 180 181 if (!found && (dflags & (CDESC_PATH_ONLY|CDESC_TYPE)) == 0) 182 sh_notfound (list->word->word); 183 184 successful_finds += found; 185 list = list->next; 186 } 187 188 fflush (stdout); 189 190 return ((successful_finds != 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE); 191} 192 193/* 194 * Describe COMMAND as required by the type and command builtins. 195 * 196 * Behavior is controlled by DFLAGS. Flag values are 197 * CDESC_ALL print all descriptions of a command 198 * CDESC_SHORTDESC print the description for type and command -V 199 * CDESC_REUSABLE print in a format that may be reused as input 200 * CDESC_TYPE print the type for type -t 201 * CDESC_PATH_ONLY print the path for type -p 202 * CDESC_FORCE_PATH force a path search for type -P 203 * CDESC_NOFUNCS skip function lookup for type -f 204 * CDESC_ABSPATH convert to absolute path, no ./ prefix 205 * 206 * CDESC_ALL says whether or not to look for all occurrences of COMMAND, or 207 * return after finding it once. 208 */ 209int 210describe_command (command, dflags) 211 char *command; 212 int dflags; 213{ 214 int found, i, found_file, f, all; 215 char *full_path, *x; 216 SHELL_VAR *func; 217#if defined (ALIAS) 218 alias_t *alias; 219#endif 220 221 all = (dflags & CDESC_ALL) != 0; 222 found = found_file = 0; 223 full_path = (char *)NULL; 224 225#if defined (ALIAS) 226 /* Command is an alias? */ 227 if (((dflags & CDESC_FORCE_PATH) == 0) && expand_aliases && (alias = find_alias (command))) 228 { 229 if (dflags & CDESC_TYPE) 230 puts ("alias"); 231 else if (dflags & CDESC_SHORTDESC) 232 printf (_("%s is aliased to `%s'\n"), command, alias->value); 233 else if (dflags & CDESC_REUSABLE) 234 { 235 x = sh_single_quote (alias->value); 236 printf ("alias %s=%s\n", command, x); 237 free (x); 238 } 239 240 found = 1; 241 242 if (all == 0) 243 return (1); 244 } 245#endif /* ALIAS */ 246 247 /* Command is a shell reserved word? */ 248 if (((dflags & CDESC_FORCE_PATH) == 0) && (i = find_reserved_word (command)) >= 0) 249 { 250 if (dflags & CDESC_TYPE) 251 puts ("keyword"); 252 else if (dflags & CDESC_SHORTDESC) 253 printf (_("%s is a shell keyword\n"), command); 254 else if (dflags & CDESC_REUSABLE) 255 printf ("%s\n", command); 256 257 found = 1; 258 259 if (all == 0) 260 return (1); 261 } 262 263 /* Command is a function? */ 264 if (((dflags & (CDESC_FORCE_PATH|CDESC_NOFUNCS)) == 0) && (func = find_function (command))) 265 { 266 if (dflags & CDESC_TYPE) 267 puts ("function"); 268 else if (dflags & CDESC_SHORTDESC) 269 { 270#define PRETTY_PRINT_FUNC 1 271 char *result; 272 273 printf (_("%s is a function\n"), command); 274 275 /* We're blowing away THE_PRINTED_COMMAND here... */ 276 277 result = named_function_string (command, 278 (COMMAND *) function_cell (func), 279 PRETTY_PRINT_FUNC); 280 printf ("%s\n", result); 281#undef PRETTY_PRINT_FUNC 282 } 283 else if (dflags & CDESC_REUSABLE) 284 printf ("%s\n", command); 285 286 found = 1; 287 288 if (all == 0) 289 return (1); 290 } 291 292 /* Command is a builtin? */ 293 if (((dflags & CDESC_FORCE_PATH) == 0) && find_shell_builtin (command)) 294 { 295 if (dflags & CDESC_TYPE) 296 puts ("builtin"); 297 else if (dflags & CDESC_SHORTDESC) 298 printf (_("%s is a shell builtin\n"), command); 299 else if (dflags & CDESC_REUSABLE) 300 printf ("%s\n", command); 301 302 found = 1; 303 304 if (all == 0) 305 return (1); 306 } 307 308 /* Command is a disk file? */ 309 /* If the command name given is already an absolute command, just 310 check to see if it is executable. */ 311 if (absolute_program (command)) 312 { 313 f = file_status (command); 314 if (f & FS_EXECABLE) 315 { 316 if (dflags & CDESC_TYPE) 317 puts ("file"); 318 else if (dflags & CDESC_SHORTDESC) 319 printf (_("%s is %s\n"), command, command); 320 else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY)) 321 printf ("%s\n", command); 322 323 /* There's no use looking in the hash table or in $PATH, 324 because they're not consulted when an absolute program 325 name is supplied. */ 326 return (1); 327 } 328 } 329 330 /* If the user isn't doing "-a", then we might care about 331 whether the file is present in our hash table. */ 332 if (all == 0 || (dflags & CDESC_FORCE_PATH)) 333 { 334 if (full_path = phash_search (command)) 335 { 336 if (dflags & CDESC_TYPE) 337 puts ("file"); 338 else if (dflags & CDESC_SHORTDESC) 339 printf (_("%s is hashed (%s)\n"), command, full_path); 340 else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY)) 341 printf ("%s\n", full_path); 342 343 free (full_path); 344 return (1); 345 } 346 } 347 348 /* Now search through $PATH. */ 349 while (1) 350 { 351 if (all == 0) 352 full_path = find_user_command (command); 353 else 354 full_path = 355 user_command_matches (command, FS_EXEC_ONLY, found_file); 356 /* XXX - should that be FS_EXEC_PREFERRED? */ 357 358 if (!full_path) 359 break; 360 361 /* If we found the command as itself by looking through $PATH, it 362 probably doesn't exist. Check whether or not the command is an 363 executable file. If it's not, don't report a match. This is 364 the default posix mode behavior */ 365 if (STREQ (full_path, command) || posixly_correct) 366 { 367 f = file_status (full_path); 368 if ((f & FS_EXECABLE) == 0) 369 { 370 free (full_path); 371 full_path = (char *)NULL; 372 if (all == 0) 373 break; 374 } 375 else if (ABSPATH (full_path)) 376 ; /* placeholder; don't need to do anything yet */ 377 else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY|CDESC_SHORTDESC)) 378 { 379 f = MP_DOCWD | ((dflags & CDESC_ABSPATH) ? MP_RMDOT : 0); 380 full_path = sh_makepath ((char *)NULL, full_path, f); 381 } 382 } 383 /* If we require a full path and don't have one, make one */ 384 else if ((dflags & CDESC_ABSPATH) && ABSPATH (full_path) == 0) 385 full_path = sh_makepath ((char *)NULL, full_path, MP_DOCWD|MP_RMDOT); 386 387 found_file++; 388 found = 1; 389 390 if (dflags & CDESC_TYPE) 391 puts ("file"); 392 else if (dflags & CDESC_SHORTDESC) 393 printf ("%s is %s\n", command, full_path); 394 else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY)) 395 printf ("%s\n", full_path); 396 397 free (full_path); 398 full_path = (char *)NULL; 399 400 if (all == 0) 401 break; 402 } 403 404 return (found); 405} 406