]> git.ipfire.org Git - thirdparty/bash.git/blame_incremental - builtins/hash.def
Bash-5.3: updated translations and gettext gmo files
[thirdparty/bash.git] / builtins / hash.def
... / ...
CommitLineData
1This file is hash.def, from which is created hash.c.
2It implements the builtin "hash" in Bash.
3
4Copyright (C) 1987-2024 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
71static int add_hashed_command (char *, int);
72static int print_hash_info (BUCKET_CONTENTS *);
73static int print_portable_hash_info (BUCKET_CONTENTS *);
74static int print_hashed_commands (int);
75static 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. */
80int
81hash_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
213static int
214add_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. */
238static int
239print_hash_info (BUCKET_CONTENTS *item)
240{
241 printf ("%4d\t%s\n", item->times_found, pathdata(item)->path);
242 return 0;
243}
244
245static int
246print_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
260static int
261print_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
272static int
273list_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}