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