Line data Source code
1 : /* hashcmd.c - functions for managing a hash table mapping command names to 2 : full pathnames. */ 3 : 4 : /* Copyright (C) 1997-2009 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 : 22 : #include <config.h> 23 : 24 : #include "bashtypes.h" 25 : #include "posixstat.h" 26 : 27 : #if defined (HAVE_UNISTD_H) 28 : # include <unistd.h> 29 : #endif 30 : 31 : #include "bashansi.h" 32 : 33 : #include "shell.h" 34 : #include "findcmd.h" 35 : #include "hashcmd.h" 36 : 37 : extern int hashing_enabled; 38 : 39 : HASH_TABLE *hashed_filenames = (HASH_TABLE *)NULL; 40 : 41 : static void phash_freedata __P((PTR_T)); 42 : 43 : void 44 0 : phash_create () 45 : { 46 0 : if (hashed_filenames == 0) 47 44266 : hashed_filenames = hash_create (FILENAME_HASH_BUCKETS); 48 0 : } 49 : 50 : static void 51 0 : phash_freedata (data) 52 : PTR_T data; 53 : { 54 0 : free (((PATH_DATA *)data)->path); 55 0 : free (data); 56 0 : } 57 : 58 : void 59 0 : phash_flush () 60 : { 61 0 : if (hashed_filenames) 62 0 : hash_flush (hashed_filenames, phash_freedata); 63 0 : } 64 : 65 : /* Remove FILENAME from the table of hashed commands. */ 66 : int 67 44 : phash_remove (filename) 68 : const char *filename; 69 : { 70 44 : register BUCKET_CONTENTS *item; 71 : 72 44 : if (hashing_enabled == 0 || hashed_filenames == 0) 73 : return 0; 74 : 75 0 : item = hash_remove (filename, hashed_filenames, 0); 76 0 : if (item) 77 : { 78 0 : if (item->data) 79 0 : phash_freedata (item->data); 80 0 : free (item->key); 81 0 : free (item); 82 0 : return 0; 83 : } 84 : return 1; 85 : } 86 : 87 : /* Place FILENAME (key) and FULL_PATH (data->path) into the 88 : hash table. CHECK_DOT if non-null is for future calls to 89 : phash_search (); it means that this file was found 90 : in a directory in $PATH that is not an absolute pathname. 91 : FOUND is the initial value for times_found. */ 92 : void 93 47222 : phash_insert (filename, full_path, check_dot, found) 94 : char *filename, *full_path; 95 : int check_dot, found; 96 : { 97 47222 : register BUCKET_CONTENTS *item; 98 : 99 47222 : if (hashing_enabled == 0) 100 : return; 101 : 102 47222 : if (hashed_filenames == 0) 103 44266 : phash_create (); 104 : 105 47222 : item = hash_insert (filename, hashed_filenames, 0); 106 47222 : if (item->data) 107 0 : free (pathdata(item)->path); 108 : else 109 : { 110 47222 : item->key = savestring (filename); 111 47222 : item->data = xmalloc (sizeof (PATH_DATA)); 112 : } 113 47222 : pathdata(item)->path = savestring (full_path); 114 47222 : pathdata(item)->flags = 0; 115 47222 : if (check_dot) 116 0 : pathdata(item)->flags |= HASH_CHKDOT; 117 47222 : if (*full_path != '/') 118 0 : pathdata(item)->flags |= HASH_RELPATH; 119 47222 : item->times_found = found; 120 : } 121 : 122 : /* Return the full pathname that FILENAME hashes to. If FILENAME 123 : is hashed, but (data->flags & HASH_CHKDOT) is non-zero, check 124 : ./FILENAME and return that if it is executable. This always 125 : returns a newly-allocated string; the caller is responsible 126 : for freeing it. */ 127 : char * 128 7787945 : phash_search (filename) 129 : const char *filename; 130 : { 131 7787945 : register BUCKET_CONTENTS *item; 132 7787945 : char *path, *dotted_filename, *tail; 133 7787945 : int same; 134 : 135 7787945 : if (hashing_enabled == 0 || hashed_filenames == 0) 136 : return ((char *)NULL); 137 : 138 60464 : item = hash_search (filename, hashed_filenames, 0); 139 : 140 60464 : if (item == NULL) 141 : return ((char *)NULL); 142 : 143 : /* If this filename is hashed, but `.' comes before it in the path, 144 : see if ./filename is executable. If the hashed value is not an 145 : absolute pathname, see if ./`hashed-value' exists. */ 146 25 : path = pathdata(item)->path; 147 25 : if (pathdata(item)->flags & (HASH_CHKDOT|HASH_RELPATH)) 148 : { 149 0 : tail = (pathdata(item)->flags & HASH_RELPATH) ? path : (char *)filename; /* XXX - fix const later */ 150 : /* If the pathname does not start with a `./', add a `./' to it. */ 151 0 : if (tail[0] != '.' || tail[1] != '/') 152 : { 153 0 : dotted_filename = (char *)xmalloc (3 + strlen (tail)); 154 0 : dotted_filename[0] = '.'; dotted_filename[1] = '/'; 155 0 : strcpy (dotted_filename + 2, tail); 156 : } 157 : else 158 0 : dotted_filename = savestring (tail); 159 : 160 0 : if (executable_file (dotted_filename)) 161 : return (dotted_filename); 162 : 163 0 : free (dotted_filename); 164 : 165 : #if 0 166 : if (pathdata(item)->flags & HASH_RELPATH) 167 : return ((char *)NULL); 168 : #endif 169 : 170 : /* Watch out. If this file was hashed to "./filename", and 171 : "./filename" is not executable, then return NULL. */ 172 : 173 : /* Since we already know "./filename" is not executable, what 174 : we're really interested in is whether or not the `path' 175 : portion of the hashed filename is equivalent to the current 176 : directory, but only if it starts with a `.'. (This catches 177 : ./. and so on.) same_file () tests general Unix file 178 : equivalence -- same device and inode. */ 179 0 : if (*path == '.') 180 : { 181 0 : same = 0; 182 0 : tail = (char *)strrchr (path, '/'); 183 : 184 0 : if (tail) 185 : { 186 0 : *tail = '\0'; 187 0 : same = same_file (".", path, (struct stat *)NULL, (struct stat *)NULL); 188 0 : *tail = '/'; 189 : } 190 : 191 0 : return same ? (char *)NULL : savestring (path); 192 : } 193 : } 194 : 195 25 : return (savestring (path)); 196 : }