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