1 /* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */ 2 3 /* Copyright (C) 1988,1989 Free Software Foundation, Inc. 4 5 This file is part of GNU Readline, a library for reading lines 6 of text with interactive input and history editing. 7 8 Readline is free software; you can redistribute it and/or modify it 9 under the terms of the GNU General Public License as published by the 10 Free Software Foundation; either version 2, or (at your option) any 11 later version. 12 13 Readline is distributed in the hope that it will be useful, but 14 WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with Readline; see the file COPYING. If not, write to the Free 20 Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ 21 22 #if defined (HAVE_CONFIG_H) 23 # include <config.h> 24 #endif 25 26 #if defined (HAVE_UNISTD_H) 27 # ifdef _MINIX 28 # include <sys/types.h> 29 # endif 30 # include <unistd.h> 31 #endif 32 33 #if defined (HAVE_STRING_H) 34 # include <string.h> 35 #else /* !HAVE_STRING_H */ 36 # include <strings.h> 37 #endif /* !HAVE_STRING_H */ 38 39 #if defined (HAVE_STDLIB_H) 40 # include <stdlib.h> 41 #else 42 # include "ansi_stdlib.h" 43 #endif /* HAVE_STDLIB_H */ 44 45 #include <sys/types.h> 46 #include <pwd.h> 47 48 #include "tilde.h" 49 50 #if defined (TEST) || defined (STATIC_MALLOC) 51 static char *xmalloc (), *xrealloc (); 52 #else 53 # if defined __STDC__ 54 extern char *xmalloc (int); 55 extern char *xrealloc (void *, int); 56 # else 57 extern char *xmalloc (), *xrealloc (); 58 # endif /* !__STDC__ */ 59 #endif /* TEST || STATIC_MALLOC */ 60 61 #if !defined (HAVE_GETPW_DECLS) 62 extern struct passwd *getpwuid (), *getpwnam (); 63 #endif /* !HAVE_GETPW_DECLS */ 64 65 #if !defined (savestring) 66 #include <stdio.h> 67 static char * 68 xstrdup(char *s) 69 { 70 char * cp; 71 cp = strdup(s); 72 if (cp == NULL) { 73 fprintf (stderr, "xstrdup: out of virtual memory\n"); 74 exit (2); 75 } 76 return(cp); 77 } 78 #define savestring(x) xstrdup(x) 79 #endif /* !savestring */ 80 81 #if !defined (NULL) 82 # if defined (__STDC__) 83 # define NULL ((void *) 0) 84 # else 85 # define NULL 0x0 86 # endif /* !__STDC__ */ 87 #endif /* !NULL */ 88 89 /* If being compiled as part of bash, these will be satisfied from 90 variables.o. If being compiled as part of readline, they will 91 be satisfied from shell.o. */ 92 extern char *get_home_dir __P((void)); 93 extern char *get_env_value __P((char *)); 94 95 /* The default value of tilde_additional_prefixes. This is set to 96 whitespace preceding a tilde so that simple programs which do not 97 perform any word separation get desired behaviour. */ 98 static char *default_prefixes[] = 99 { " ~", "\t~", (char *)NULL }; 100 101 /* The default value of tilde_additional_suffixes. This is set to 102 whitespace or newline so that simple programs which do not 103 perform any word separation get desired behaviour. */ 104 static char *default_suffixes[] = 105 { " ", "\n", (char *)NULL }; 106 107 /* If non-null, this contains the address of a function that the application 108 wants called before trying the standard tilde expansions. The function 109 is called with the text sans tilde, and returns a malloc()'ed string 110 which is the expansion, or a NULL pointer if the expansion fails. */ 111 CPFunction *tilde_expansion_preexpansion_hook = (CPFunction *)NULL; 112 113 /* If non-null, this contains the address of a function to call if the 114 standard meaning for expanding a tilde fails. The function is called 115 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string 116 which is the expansion, or a NULL pointer if there is no expansion. */ 117 CPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL; 118 119 /* When non-null, this is a NULL terminated array of strings which 120 are duplicates for a tilde prefix. Bash uses this to expand 121 `=~' and `:~'. */ 122 char **tilde_additional_prefixes = default_prefixes; 123 124 /* When non-null, this is a NULL terminated array of strings which match 125 the end of a username, instead of just "/". Bash sets this to 126 `:' and `=~'. */ 127 char **tilde_additional_suffixes = default_suffixes; 128 129 /* Find the start of a tilde expansion in STRING, and return the index of 130 the tilde which starts the expansion. Place the length of the text 131 which identified this tilde starter in LEN, excluding the tilde itself. */ 132 static int 133 tilde_find_prefix (string, len) 134 char *string; 135 int *len; 136 { 137 register int i, j, string_len; 138 register char **prefixes; 139 140 prefixes = tilde_additional_prefixes; 141 142 string_len = strlen (string); 143 *len = 0; 144 145 if (*string == '\0' || *string == '~') 146 return (0); 147 148 if (prefixes) 149 { 150 for (i = 0; i < string_len; i++) 151 { 152 for (j = 0; prefixes[j]; j++) 153 { 154 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) 155 { 156 *len = strlen (prefixes[j]) - 1; 157 return (i + *len); 158 } 159 } 160 } 161 } 162 return (string_len); 163 } 164 165 /* Find the end of a tilde expansion in STRING, and return the index of 166 the character which ends the tilde definition. */ 167 static int 168 tilde_find_suffix (string) 169 char *string; 170 { 171 register int i, j, string_len; 172 register char **suffixes; 173 174 suffixes = tilde_additional_suffixes; 175 string_len = strlen (string); 176 177 for (i = 0; i < string_len; i++) 178 { 179 #if defined (__MSDOS__) 180 if (string[i] == '/' || string[i] == '\\' /* || !string[i] */) 181 #else 182 if (string[i] == '/' /* || !string[i] */) 183 #endif 184 break; 185 186 for (j = 0; suffixes && suffixes[j]; j++) 187 { 188 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) 189 return (i); 190 } 191 } 192 return (i); 193 } 194 195 /* Return a new string which is the result of tilde expanding STRING. */ 196 char * 197 tilde_expand (string) 198 char *string; 199 { 200 char *result; 201 int result_size, result_index; 202 203 result_index = result_size = 0; 204 if (result = strchr (string, '~')) 205 result = xmalloc (result_size = (strlen (string) + 16)); 206 else 207 result = xmalloc (result_size = (strlen (string) + 1)); 208 209 /* Scan through STRING expanding tildes as we come to them. */ 210 while (1) 211 { 212 register int start, end; 213 char *tilde_word, *expansion; 214 int len; 215 216 /* Make START point to the tilde which starts the expansion. */ 217 start = tilde_find_prefix (string, &len); 218 219 /* Copy the skipped text into the result. */ 220 if ((result_index + start + 1) > result_size) 221 result = xrealloc (result, 1 + (result_size += (start + 20))); 222 223 strncpy (result + result_index, string, start); 224 result_index += start; 225 226 /* Advance STRING to the starting tilde. */ 227 string += start; 228 229 /* Make END be the index of one after the last character of the 230 username. */ 231 end = tilde_find_suffix (string); 232 233 /* If both START and END are zero, we are all done. */ 234 if (!start && !end) 235 break; 236 237 /* Expand the entire tilde word, and copy it into RESULT. */ 238 tilde_word = xmalloc (1 + end); 239 strncpy (tilde_word, string, end); 240 tilde_word[end] = '\0'; 241 string += end; 242 243 expansion = tilde_expand_word (tilde_word); 244 free (tilde_word); 245 246 len = strlen (expansion); 247 #ifdef __CYGWIN32__ 248 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when 249 $HOME for `user' is /. On cygwin, // denotes a network drive. */ 250 if (len > 1 || *expansion != '/' || *string != '/') 251 #endif 252 { 253 if ((result_index + len + 1) > result_size) 254 result = xrealloc (result, 1 + (result_size += (len + 20))); 255 256 strlcpy (result + result_index, expansion, 257 result_size - result_index); 258 result_index += len; 259 } 260 free (expansion); 261 } 262 263 result[result_index] = '\0'; 264 265 return (result); 266 } 267 268 /* Take FNAME and return the tilde prefix we want expanded. If LENP is 269 non-null, the index of the end of the prefix into FNAME is returned in 270 the location it points to. */ 271 static char * 272 isolate_tilde_prefix (fname, lenp) 273 char *fname; 274 int *lenp; 275 { 276 char *ret; 277 int i; 278 279 ret = xmalloc (strlen (fname)); 280 #if defined (__MSDOS__) 281 for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++) 282 #else 283 for (i = 1; fname[i] && fname[i] != '/'; i++) 284 #endif 285 ret[i - 1] = fname[i]; 286 ret[i - 1] = '\0'; 287 if (lenp) 288 *lenp = i; 289 return ret; 290 } 291 292 /* Return a string that is PREFIX concatenated with SUFFIX starting at 293 SUFFIND. */ 294 static char * 295 glue_prefix_and_suffix (prefix, suffix, suffind) 296 char *prefix, *suffix; 297 int suffind; 298 { 299 char *ret; 300 int plen, slen; 301 302 plen = (prefix && *prefix) ? strlen (prefix) : 0; 303 slen = strlen (suffix + suffind); 304 ret = xmalloc (plen + slen + 1); 305 if (plen) 306 strlcpy (ret, prefix, plen + slen + 1); 307 strlcat (ret, suffix + suffind, plen + slen + 1); 308 return ret; 309 } 310 311 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a 312 tilde. If there is no expansion, call tilde_expansion_failure_hook. 313 This always returns a newly-allocated string, never static storage. */ 314 char * 315 tilde_expand_word (filename) 316 char *filename; 317 { 318 char *dirname, *expansion, *username; 319 int user_len; 320 struct passwd *user_entry; 321 322 if (filename == 0) 323 return ((char *)NULL); 324 325 if (*filename != '~') 326 return (savestring (filename)); 327 328 /* A leading `~/' or a bare `~' is *always* translated to the value of 329 $HOME or the home directory of the current user, regardless of any 330 preexpansion hook. */ 331 if (filename[1] == '\0' || filename[1] == '/') 332 { 333 /* Prefix $HOME to the rest of the string. */ 334 expansion = get_env_value ("HOME"); 335 336 /* If there is no HOME variable, look up the directory in 337 the password database. */ 338 if (expansion == 0 || *expansion == '\0') 339 expansion = get_home_dir (); 340 341 return (glue_prefix_and_suffix (expansion, filename, 1)); 342 } 343 344 username = isolate_tilde_prefix (filename, &user_len); 345 346 if (tilde_expansion_preexpansion_hook) 347 { 348 expansion = (*tilde_expansion_preexpansion_hook) (username); 349 if (expansion) 350 { 351 dirname = glue_prefix_and_suffix (expansion, filename, user_len); 352 free (username); 353 free (expansion); 354 return (dirname); 355 } 356 } 357 358 /* No preexpansion hook, or the preexpansion hook failed. Look in the 359 password database. */ 360 dirname = (char *)NULL; 361 user_entry = getpwnam (username); 362 if (user_entry == 0) 363 { 364 /* If the calling program has a special syntax for expanding tildes, 365 and we couldn't find a standard expansion, then let them try. */ 366 if (tilde_expansion_failure_hook) 367 { 368 expansion = (*tilde_expansion_failure_hook) (username); 369 if (expansion) 370 { 371 dirname = glue_prefix_and_suffix (expansion, filename, user_len); 372 free (expansion); 373 } 374 } 375 free (username); 376 /* If we don't have a failure hook, or if the failure hook did not 377 expand the tilde, return a copy of what we were passed. */ 378 if (dirname == 0) 379 dirname = savestring (filename); 380 } 381 else 382 { 383 free (username); 384 dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len); 385 } 386 387 endpwent (); 388 return (dirname); 389 } 390 391 392 #if defined (TEST) 393 #undef NULL 394 #include <stdio.h> 395 396 main (argc, argv) 397 int argc; 398 char **argv; 399 { 400 char *result, line[512]; 401 int done = 0; 402 403 while (!done) 404 { 405 printf ("~expand: "); 406 fflush (stdout); 407 408 if (!fgets (line, sizeof(line), stdin)) 409 strlcpy (line, "done", sizeof(line)); 410 else if (line[strlen(line) - 1] == '\n') 411 line[strlen(line) - 1] = '\0'; 412 if ((strcmp (line, "done") == 0) || 413 (strcmp (line, "quit") == 0) || 414 (strcmp (line, "exit") == 0)) 415 { 416 done = 1; 417 break; 418 } 419 420 result = tilde_expand (line); 421 printf (" --> %s\n", result); 422 free (result); 423 } 424 exit (0); 425 } 426 427 static void memory_error_and_abort (); 428 429 static char * 430 xmalloc (bytes) 431 int bytes; 432 { 433 char *temp = (char *)malloc (bytes); 434 435 if (!temp) 436 memory_error_and_abort (); 437 return (temp); 438 } 439 440 static char * 441 xrealloc (pointer, bytes) 442 char *pointer; 443 int bytes; 444 { 445 char *temp; 446 447 if (!pointer) 448 temp = (char *)malloc (bytes); 449 else 450 temp = (char *)realloc (pointer, bytes); 451 452 if (!temp) 453 memory_error_and_abort (); 454 455 return (temp); 456 } 457 458 static void 459 memory_error_and_abort () 460 { 461 fprintf (stderr, "readline: out of virtual memory\n"); 462 abort (); 463 } 464 465 /* 466 * Local variables: 467 * compile-command: "gcc -g -DTEST -o tilde tilde.c" 468 * end: 469 */ 470 #endif /* TEST */ 471