]> git.ipfire.org Git - thirdparty/bash.git/blob - builtins/hash.def
fc96d59460f2b2ad75ba26f3d4a3b6e47c4e3024
[thirdparty/bash.git] / builtins / hash.def
1 This file is hash.def, from which is created hash.c.
2 It implements the builtin "hash" in Bash.
3
4 Copyright (C) 1987-2024 Free Software Foundation, Inc.
5
6 This file is part of GNU Bash, the Bourne Again SHell.
7
8 Bash 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.
12
13 Bash 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.
17
18 You should have received a copy of the GNU General Public License
19 along 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 ...]
26 Remember or display program locations.
27
28 Determine and remember the full pathname of each command NAME. If
29 no arguments are given, information about remembered commands is displayed.
30
31 Options:
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
39 Arguments:
40 NAME Each NAME is searched for in $PATH and added to the list
41 of remembered commands.
42
43 Exit Status:
44 Returns 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
71 static int add_hashed_command (char *, int);
72 static int print_hash_info (BUCKET_CONTENTS *);
73 static int print_portable_hash_info (BUCKET_CONTENTS *);
74 static int print_hashed_commands (int);
75 static int list_hashed_filename_targets (WORD_LIST *, int);
76
77 /* Print statistics on the current state of hashed commands. If LIST is
78 not empty, then rehash (or hash in the first place) the specified
79 commands. */
80 int
81 hash_builtin (WORD_LIST *list)
82 {
83 int expunge_hash_table, list_targets, list_portably, delete, opt;
84 char *w, *pathname;
85
86 if (hashing_enabled == 0)
87 {
88 builtin_error (_("hashing disabled"));
89 return (EXECUTION_FAILURE);
90 }
91
92 expunge_hash_table = list_targets = list_portably = delete = 0;
93 pathname = (char *)NULL;
94 reset_internal_getopt ();
95 while ((opt = internal_getopt (list, "dlp:rt")) != -1)
96 {
97 switch (opt)
98 {
99 case 'd':
100 delete = 1;
101 break;
102 case 'l':
103 list_portably = 1;
104 break;
105 case 'p':
106 pathname = list_optarg;
107 break;
108 case 'r':
109 expunge_hash_table = 1;
110 break;
111 case 't':
112 list_targets = 1;
113 break;
114 CASE_HELPOPT;
115 default:
116 builtin_usage ();
117 return (EX_USAGE);
118 }
119 }
120 list = loptend;
121
122 /* hash -t requires at least one argument. */
123 if (list == 0 && (delete || list_targets))
124 {
125 sh_needarg (delete ? "-d" : "-t");
126 return (sh_chkwrite (EXECUTION_FAILURE));
127 }
128
129 /* It's an error to specify a pathname to hash to, but no name to hash. */
130 if (pathname && list == 0)
131 {
132 builtin_usage ();
133 return (EX_USAGE);
134 }
135
136 /* We want hash -r to be silent, but hash -- to print hashing info, so
137 we test expunge_hash_table. */
138
139 if (list == 0 && expunge_hash_table == 0)
140 {
141 opt = print_hashed_commands (list_portably);
142 if (opt == 0 && posixly_correct == 0 &&
143 (list_portably == 0 || shell_compatibility_level <= 50))
144 printf (_("%s: hash table empty\n"), this_command_name);
145
146 return (sh_chkwrite (EXECUTION_SUCCESS));
147 }
148
149 if (expunge_hash_table)
150 phash_flush ();
151
152 /* If someone runs `hash -r -t xyz' he will be disappointed. */
153 if (list_targets)
154 return (list_hashed_filename_targets (list, list_portably));
155
156 #if defined (RESTRICTED_SHELL)
157 if (restricted && pathname)
158 {
159 if (absolute_program (pathname))
160 {
161 sh_restricted (pathname);
162 return (EXECUTION_FAILURE);
163 }
164 /* If we are changing the hash table in a restricted shell, make sure the
165 target pathname can be found using a $PATH search. */
166 w = find_user_command (pathname);
167 if (w == 0 || *w == 0 || executable_file (w) == 0)
168 {
169 sh_notfound (pathname);
170 free (w);
171 return (EXECUTION_FAILURE);
172 }
173 free (w);
174 }
175 #endif
176
177 for (opt = EXECUTION_SUCCESS; list; list = list->next)
178 {
179 /* Add, remove or rehash the specified commands. */
180 w = list->word->word;
181 if (absolute_program (w))
182 continue;
183 else if (pathname)
184 {
185 if (file_isdir (pathname))
186 {
187 #ifdef EISDIR
188 builtin_error ("%s: %s", pathname, strerror (EISDIR));
189 #else
190 builtin_error (_("%s: is a directory"), pathname);
191 #endif
192 opt = EXECUTION_FAILURE;
193 }
194 else
195 phash_insert (w, pathname, 0, 0);
196 }
197 else if (delete)
198 {
199 if (phash_remove (w))
200 {
201 sh_notfound (w);
202 opt = EXECUTION_FAILURE;
203 }
204 }
205 else if (add_hashed_command (w, 0))
206 opt = EXECUTION_FAILURE;
207 }
208
209 fflush (stdout);
210 return (opt);
211 }
212
213 static int
214 add_hashed_command (char *w, int quiet)
215 {
216 int rv;
217 char *full_path;
218
219 rv = 0;
220 if (find_function (w) == 0 && find_shell_builtin (w) == 0)
221 {
222 phash_remove (w);
223 full_path = find_user_command (w);
224 if (full_path && executable_file (full_path))
225 phash_insert (w, full_path, dot_found_in_search, 0);
226 else
227 {
228 if (quiet == 0)
229 sh_notfound (w);
230 rv++;
231 }
232 FREE (full_path);
233 }
234 return (rv);
235 }
236
237 /* Print information about current hashed info. */
238 static int
239 print_hash_info (BUCKET_CONTENTS *item)
240 {
241 printf ("%4d\t%s\n", item->times_found, pathdata(item)->path);
242 return 0;
243 }
244
245 static int
246 print_portable_hash_info (BUCKET_CONTENTS *item)
247 {
248 char *fp, *fn;
249
250 fp = printable_filename (pathdata(item)->path, 1);
251 fn = printable_filename (item->key, 1);
252 printf ("builtin hash -p %s %s\n", fp, fn);
253 if (fp != pathdata(item)->path)
254 free (fp);
255 if (fn != item->key)
256 free (fn);
257 return 0;
258 }
259
260 static int
261 print_hashed_commands (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
272 static int
273 list_hashed_filename_targets (WORD_LIST *list, int fmt)
274 {
275 int all_found, multiple;
276 char *target;
277 WORD_LIST *l;
278
279 all_found = 1;
280 multiple = list->next != 0;
281
282 for (l = list; l; l = l->next)
283 {
284 target = phash_search (l->word->word);
285 if (target == 0)
286 {
287 all_found = 0;
288 sh_notfound (l->word->word);
289 continue;
290 }
291 if (fmt)
292 printf ("builtin hash -p %s %s\n", target, l->word->word);
293 else
294 {
295 if (multiple)
296 printf ("%s\t", l->word->word);
297 printf ("%s\n", target);
298 }
299 free (target);
300 }
301
302 return (sh_chkwrite (all_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE));
303 }