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