1 /* tilde.c -- tilde expansion code (~/foo := $HOME/foo). 2 $Id: tilde.c,v 1.3 2000/02/09 02:18:40 espie Exp $ 3 4 Copyright (C) 1988, 89, 90, 91, 92, 93, 96, 98, 99 5 Free Software Foundation, Inc. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 21 Written by Brian Fox (bfox@ai.mit.edu). */ 22 23 /* Include config.h before doing alloca. */ 24 #include "info.h" 25 26 #if defined (TEST) || defined (STATIC_MALLOC) 27 static void *xmalloc (), *xrealloc (); 28 #endif /* TEST || STATIC_MALLOC */ 29 30 /* The default value of tilde_additional_prefixes. This is set to 31 whitespace preceding a tilde so that simple programs which do not 32 perform any word separation get desired behaviour. */ 33 static char *default_prefixes[] = 34 { " ~", "\t~", (char *)NULL }; 35 36 /* The default value of tilde_additional_suffixes. This is set to 37 whitespace or newline so that simple programs which do not 38 perform any word separation get desired behaviour. */ 39 static char *default_suffixes[] = 40 { " ", "\n", (char *)NULL }; 41 42 /* If non-null, this contains the address of a function to call if the 43 standard meaning for expanding a tilde fails. The function is called 44 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string 45 which is the expansion, or a NULL pointer if there is no expansion. */ 46 CFunction *tilde_expansion_failure_hook = (CFunction *)NULL; 47 48 /* When non-null, this is a NULL terminated array of strings which 49 are duplicates for a tilde prefix. Bash uses this to expand 50 `=~' and `:~'. */ 51 char **tilde_additional_prefixes = default_prefixes; 52 53 /* When non-null, this is a NULL terminated array of strings which match 54 the end of a username, instead of just "/". Bash sets this to 55 `:' and `=~'. */ 56 char **tilde_additional_suffixes = default_suffixes; 57 58 /* Find the start of a tilde expansion in STRING, and return the index of 59 the tilde which starts the expansion. Place the length of the text 60 which identified this tilde starter in LEN, excluding the tilde itself. */ 61 static int 62 tilde_find_prefix (string, len) 63 char *string; 64 int *len; 65 { 66 register int i, j, string_len; 67 register char **prefixes = tilde_additional_prefixes; 68 69 string_len = strlen (string); 70 *len = 0; 71 72 if (!*string || *string == '~') 73 return (0); 74 75 if (prefixes) 76 { 77 for (i = 0; i < string_len; i++) 78 { 79 for (j = 0; prefixes[j]; j++) 80 { 81 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) 82 { 83 *len = strlen (prefixes[j]) - 1; 84 return (i + *len); 85 } 86 } 87 } 88 } 89 return (string_len); 90 } 91 92 /* Find the end of a tilde expansion in STRING, and return the index of 93 the character which ends the tilde definition. */ 94 static int 95 tilde_find_suffix (string) 96 char *string; 97 { 98 register int i, j, string_len; 99 register char **suffixes = tilde_additional_suffixes; 100 101 string_len = strlen (string); 102 103 for (i = 0; i < string_len; i++) 104 { 105 if (IS_SLASH (string[i]) || !string[i]) 106 break; 107 108 for (j = 0; suffixes && suffixes[j]; j++) 109 { 110 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) 111 return (i); 112 } 113 } 114 return (i); 115 } 116 117 /* Return a new string which is the result of tilde expanding STRING. */ 118 char * 119 tilde_expand (string) 120 char *string; 121 { 122 char *result, *tilde_expand_word (); 123 int result_size, result_index; 124 125 result_size = result_index = 0; 126 result = (char *)NULL; 127 128 /* Scan through STRING expanding tildes as we come to them. */ 129 while (1) 130 { 131 register int start, end; 132 char *tilde_word, *expansion; 133 int len; 134 135 /* Make START point to the tilde which starts the expansion. */ 136 start = tilde_find_prefix (string, &len); 137 138 /* Copy the skipped text into the result. */ 139 if ((result_index + start + 1) > result_size) 140 result = (char *)xrealloc (result, 1 + (result_size += (start + 20))); 141 142 strncpy (result + result_index, string, start); 143 result_index += start; 144 145 /* Advance STRING to the starting tilde. */ 146 string += start; 147 148 /* Make END be the index of one after the last character of the 149 username. */ 150 end = tilde_find_suffix (string); 151 152 /* If both START and END are zero, we are all done. */ 153 if (!start && !end) 154 break; 155 156 /* Expand the entire tilde word, and copy it into RESULT. */ 157 tilde_word = (char *)xmalloc (1 + end); 158 strncpy (tilde_word, string, end); 159 tilde_word[end] = '\0'; 160 string += end; 161 162 expansion = tilde_expand_word (tilde_word); 163 free (tilde_word); 164 165 len = strlen (expansion); 166 if ((result_index + len + 1) > result_size) 167 result = (char *)xrealloc (result, 1 + (result_size += (len + 20))); 168 169 strcpy (result + result_index, expansion); 170 result_index += len; 171 free (expansion); 172 } 173 174 result[result_index] = '\0'; 175 176 return (result); 177 } 178 179 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a 180 tilde. If there is no expansion, call tilde_expansion_failure_hook. */ 181 char * 182 tilde_expand_word (filename) 183 char *filename; 184 { 185 char *dirname = filename ? xstrdup (filename) : NULL; 186 187 if (dirname && *dirname == '~') 188 { 189 char *temp_name; 190 if (!dirname[1] || IS_SLASH (dirname[1])) 191 { 192 /* Prepend $HOME to the rest of the string. */ 193 char *temp_home = getenv ("HOME"); 194 195 /* If there is no HOME variable, look up the directory in 196 the password database. */ 197 if (!temp_home) 198 { 199 struct passwd *entry; 200 201 entry = (struct passwd *) getpwuid (getuid ()); 202 if (entry) 203 temp_home = entry->pw_dir; 204 } 205 206 temp_name = xmalloc (1 + strlen (&dirname[1]) 207 + (temp_home ? strlen (temp_home) : 0)); 208 if (temp_home) 209 strcpy (temp_name, temp_home); 210 else 211 temp_name[0] = 0; 212 strcat (temp_name, &dirname[1]); 213 free (dirname); 214 dirname = xstrdup (temp_name); 215 free (temp_name); 216 } 217 else 218 { 219 struct passwd *user_entry; 220 char *username = xmalloc (257); 221 int i, c; 222 223 for (i = 1; (c = dirname[i]); i++) 224 { 225 if (IS_SLASH (c)) 226 break; 227 else 228 username[i - 1] = c; 229 } 230 username[i - 1] = 0; 231 232 if (!(user_entry = (struct passwd *) getpwnam (username))) 233 { 234 /* If the calling program has a special syntax for 235 expanding tildes, and we couldn't find a standard 236 expansion, then let them try. */ 237 if (tilde_expansion_failure_hook) 238 { 239 char *expansion; 240 241 expansion = (*tilde_expansion_failure_hook) (username); 242 243 if (expansion) 244 { 245 temp_name = xmalloc (1 + strlen (expansion) 246 + strlen (&dirname[i])); 247 strcpy (temp_name, expansion); 248 strcat (temp_name, &dirname[i]); 249 free (expansion); 250 goto return_name; 251 } 252 } 253 /* We shouldn't report errors. */ 254 } 255 else 256 { 257 temp_name = xmalloc (1 + strlen (user_entry->pw_dir) 258 + strlen (&dirname[i])); 259 strcpy (temp_name, user_entry->pw_dir); 260 strcat (temp_name, &dirname[i]); 261 262 return_name: 263 free (dirname); 264 dirname = xstrdup (temp_name); 265 free (temp_name); 266 } 267 268 endpwent (); 269 free (username); 270 } 271 } 272 return dirname; 273 } 274 275 276 #if defined (TEST) 277 #undef NULL 278 #include <stdio.h> 279 280 main (argc, argv) 281 int argc; 282 char **argv; 283 { 284 char *result, line[512]; 285 int done = 0; 286 287 while (!done) 288 { 289 printf ("~expand: "); 290 fflush (stdout); 291 292 if (!gets (line)) 293 strcpy (line, "done"); 294 295 if ((strcmp (line, "done") == 0) || 296 (strcmp (line, "quit") == 0) || 297 (strcmp (line, "exit") == 0)) 298 { 299 done = 1; 300 break; 301 } 302 303 result = tilde_expand (line); 304 printf (" --> %s\n", result); 305 free (result); 306 } 307 xexit (0); 308 } 309 310 static void memory_error_and_abort (); 311 312 static void * 313 xmalloc (bytes) 314 int bytes; 315 { 316 void *temp = (void *)malloc (bytes); 317 318 if (!temp) 319 memory_error_and_abort (); 320 return (temp); 321 } 322 323 static void * 324 xrealloc (pointer, bytes) 325 void *pointer; 326 int bytes; 327 { 328 void *temp; 329 330 if (!pointer) 331 temp = (char *)malloc (bytes); 332 else 333 temp = (char *)realloc (pointer, bytes); 334 335 if (!temp) 336 memory_error_and_abort (); 337 338 return (temp); 339 } 340 341 static void 342 memory_error_and_abort () 343 { 344 fprintf (stderr, _("readline: Out of virtual memory!\n")); 345 abort (); 346 } 347 #endif /* TEST */ 348 349