1This file is hash.def, from which is created hash.c. 2It implements the builtin "hash" 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 hash.c 22 23$BUILTIN hash 24$FUNCTION hash_builtin 25$SHORT_DOC hash [-lr] [-p pathname] [-dt] [name ...] 26Remember or display program locations. 27 28Determine and remember the full pathname of each command NAME. If 29no arguments are given, information about remembered commands is displayed. 30 31Options: 32 -d forget the remembered location of each NAME 33 -l display in a format that may be reused as input 34 -p pathname use PATHNAME as the full pathname of NAME 35 -r forget all remembered locations 36 -t print the remembered location of each NAME, preceding 37 each location with the corresponding NAME if multiple 38 NAMEs are given 39Arguments: 40 NAME Each NAME is searched for in $PATH and added to the list 41 of remembered commands. 42 43Exit Status: 44Returns success unless NAME is not found or an invalid option is given. 45$END 46 47#include <config.h> 48 49#include <stdio.h> 50 51#include "../bashtypes.h" 52 53#if defined (HAVE_UNISTD_H) 54# include <unistd.h> 55#endif 56 57#include <errno.h> 58 59#include "../bashansi.h" 60#include "../bashintl.h" 61 62#include "../shell.h" 63#include "../builtins.h" 64#include "../execute_cmd.h" 65#include "../flags.h" 66#include "../findcmd.h" 67#include "../hashcmd.h" 68#include "common.h" 69#include "bashgetopt.h" 70 71extern int dot_found_in_search; 72 73static int add_hashed_command PARAMS((char *, int)); 74static int print_hash_info PARAMS((BUCKET_CONTENTS *)); 75static int print_portable_hash_info PARAMS((BUCKET_CONTENTS *)); 76static int print_hashed_commands PARAMS((int)); 77static int list_hashed_filename_targets PARAMS((WORD_LIST *, int)); 78 79/* Print statistics on the current state of hashed commands. If LIST is 80 not empty, then rehash (or hash in the first place) the specified 81 commands. */ 82int 83hash_builtin (list) 84 WORD_LIST *list; 85{ 86 int expunge_hash_table, list_targets, list_portably, delete, opt; 87 char *w, *pathname; 88 89 if (hashing_enabled == 0) 90 { 91 builtin_error (_("hashing disabled")); 92 return (EXECUTION_FAILURE); 93 } 94 95 expunge_hash_table = list_targets = list_portably = delete = 0; 96 pathname = (char *)NULL; 97 reset_internal_getopt (); 98 while ((opt = internal_getopt (list, "dlp:rt")) != -1) 99 { 100 switch (opt) 101 { 102 case 'd': 103 delete = 1; 104 break; 105 case 'l': 106 list_portably = 1; 107 break; 108 case 'p': 109 pathname = list_optarg; 110 break; 111 case 'r': 112 expunge_hash_table = 1; 113 break; 114 case 't': 115 list_targets = 1; 116 break; 117 CASE_HELPOPT; 118 default: 119 builtin_usage (); 120 return (EX_USAGE); 121 } 122 } 123 list = loptend; 124 125 /* hash -t requires at least one argument. */ 126 if (list == 0 && (delete || list_targets)) 127 { 128 sh_needarg (delete ? "-d" : "-t"); 129 return (EXECUTION_FAILURE); 130 } 131 132 /* We want hash -r to be silent, but hash -- to print hashing info, so 133 we test expunge_hash_table. */ 134 if (list == 0 && expunge_hash_table == 0) 135 { 136 opt = print_hashed_commands (list_portably); 137 if (opt == 0 && posixly_correct == 0 && 138 (list_portably == 0 || shell_compatibility_level <= 50)) 139 printf (_("%s: hash table empty\n"), this_command_name); 140 141 return (EXECUTION_SUCCESS); 142 } 143 144 if (expunge_hash_table) 145 phash_flush (); 146 147 /* If someone runs `hash -r -t xyz' he will be disappointed. */ 148 if (list_targets) 149 return (list_hashed_filename_targets (list, list_portably)); 150 151#if defined (RESTRICTED_SHELL) 152 if (restricted && pathname) 153 { 154 if (strchr (pathname, '/')) 155 { 156 sh_restricted (pathname); 157 return (EXECUTION_FAILURE); 158 } 159 /* If we are changing the hash table in a restricted shell, make sure the 160 target pathname can be found using a $PATH search. */ 161 w = find_user_command (pathname); 162 if (w == 0 || *w == 0 || executable_file (w) == 0) 163 { 164 sh_notfound (pathname); 165 free (w); 166 return (EXECUTION_FAILURE); 167 } 168 free (w); 169 } 170#endif 171 172 for (opt = EXECUTION_SUCCESS; list; list = list->next) 173 { 174 /* Add, remove or rehash the specified commands. */ 175 w = list->word->word; 176 if (absolute_program (w)) 177 continue; 178 else if (pathname) 179 { 180 if (is_directory (pathname)) 181 { 182#ifdef EISDIR 183 builtin_error ("%s: %s", pathname, strerror (EISDIR)); 184#else 185 builtin_error (_("%s: is a directory"), pathname); 186#endif 187 opt = EXECUTION_FAILURE; 188 } 189 else 190 phash_insert (w, pathname, 0, 0); 191 } 192 else if (delete) 193 { 194 if (phash_remove (w)) 195 { 196 sh_notfound (w); 197 opt = EXECUTION_FAILURE; 198 } 199 } 200 else if (add_hashed_command (w, 0)) 201 opt = EXECUTION_FAILURE; 202 } 203 204 fflush (stdout); 205 return (opt); 206} 207 208static int 209add_hashed_command (w, quiet) 210 char *w; 211 int quiet; 212{ 213 int rv; 214 char *full_path; 215 216 rv = 0; 217 if (find_function (w) == 0 && find_shell_builtin (w) == 0) 218 { 219 phash_remove (w); 220 full_path = find_user_command (w); 221 if (full_path && executable_file (full_path)) 222 phash_insert (w, full_path, dot_found_in_search, 0); 223 else 224 { 225 if (quiet == 0) 226 sh_notfound (w); 227 rv++; 228 } 229 FREE (full_path); 230 } 231 return (rv); 232} 233 234/* Print information about current hashed info. */ 235static int 236print_hash_info (item) 237 BUCKET_CONTENTS *item; 238{ 239 printf ("%4d\t%s\n", item->times_found, pathdata(item)->path); 240 return 0; 241} 242 243static int 244print_portable_hash_info (item) 245 BUCKET_CONTENTS *item; 246{ 247 char *fp, *fn; 248 249 fp = printable_filename (pathdata(item)->path, 1); 250 fn = printable_filename (item->key, 1); 251 printf ("builtin hash -p %s %s\n", fp, fn); 252 if (fp != pathdata(item)->path) 253 free (fp); 254 if (fn != item->key) 255 free (fn); 256 return 0; 257} 258 259static int 260print_hashed_commands (fmt) 261 int fmt; 262{ 263 if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0) 264 return (0); 265 266 if (fmt == 0) 267 printf (_("hits\tcommand\n")); 268 hash_walk (hashed_filenames, fmt ? print_portable_hash_info : print_hash_info); 269 return (1); 270} 271 272static int 273list_hashed_filename_targets (list, fmt) 274 WORD_LIST *list; 275 int fmt; 276{ 277 int all_found, multiple; 278 char *target; 279 WORD_LIST *l; 280 281 all_found = 1; 282 multiple = list->next != 0; 283 284 for (l = list; l; l = l->next) 285 { 286 target = phash_search (l->word->word); 287 if (target == 0) 288 { 289 all_found = 0; 290 sh_notfound (l->word->word); 291 continue; 292 } 293 if (fmt) 294 printf ("builtin hash -p %s %s\n", target, l->word->word); 295 else 296 { 297 if (multiple) 298 printf ("%s\t", l->word->word); 299 printf ("%s\n", target); 300 } 301 free (target); 302 } 303 304 return (all_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE); 305} 306