1 /* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
3 /* Copyright (C) 1988,1989 Free Software Foundation, Inc.
5 This file is part of GNU Readline, a library for reading lines
6 of text with interactive input and history editing.
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
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.
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. */
22 #if defined (HAVE_CONFIG_H)
26 #if defined (HAVE_UNISTD_H)
28 # include <sys/types.h>
33 #if defined (HAVE_STRING_H)
35 #else /* !HAVE_STRING_H */
37 #endif /* !HAVE_STRING_H */
39 #if defined (HAVE_STDLIB_H)
42 # include "ansi_stdlib.h"
43 #endif /* HAVE_STDLIB_H */
45 #include <sys/types.h>
50 #if defined (TEST) || defined (STATIC_MALLOC)
51 static char *xmalloc (), *xrealloc ();
53 extern char *xmalloc
__P((int));
54 extern char *xrealloc
__P((void *, int));
55 #endif /* TEST || STATIC_MALLOC */
57 #if !defined (HAVE_GETPW_DECLS)
58 extern struct passwd
*getpwuid
__P((uid_t
));
59 extern struct passwd
*getpwnam
__P((const char *));
60 #endif /* !HAVE_GETPW_DECLS */
62 #if !defined (savestring)
64 extern char *strcpy ();
66 #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
67 #endif /* !savestring */
70 # if defined (__STDC__)
71 # define NULL ((void *) 0)
74 # endif /* !__STDC__ */
77 /* If being compiled as part of bash, these will be satisfied from
78 variables.o. If being compiled as part of readline, they will
79 be satisfied from shell.o. */
80 extern char *sh_get_home_dir
__P((void));
81 extern char *sh_get_env_value
__P((const char *));
83 /* The default value of tilde_additional_prefixes. This is set to
84 whitespace preceding a tilde so that simple programs which do not
85 perform any word separation get desired behaviour. */
86 static const char *default_prefixes
[] =
87 { " ~", "\t~", (const char *)NULL
};
89 /* The default value of tilde_additional_suffixes. This is set to
90 whitespace or newline so that simple programs which do not
91 perform any word separation get desired behaviour. */
92 static const char *default_suffixes
[] =
93 { " ", "\n", (const char *)NULL
};
95 /* If non-null, this contains the address of a function that the application
96 wants called before trying the standard tilde expansions. The function
97 is called with the text sans tilde, and returns a malloc()'ed string
98 which is the expansion, or a NULL pointer if the expansion fails. */
99 tilde_hook_func_t
*tilde_expansion_preexpansion_hook
= (tilde_hook_func_t
*)NULL
;
101 /* If non-null, this contains the address of a function to call if the
102 standard meaning for expanding a tilde fails. The function is called
103 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
104 which is the expansion, or a NULL pointer if there is no expansion. */
105 tilde_hook_func_t
*tilde_expansion_failure_hook
= (tilde_hook_func_t
*)NULL
;
107 /* When non-null, this is a NULL terminated array of strings which
108 are duplicates for a tilde prefix. Bash uses this to expand
110 char **tilde_additional_prefixes
= (char **)default_prefixes
;
112 /* When non-null, this is a NULL terminated array of strings which match
113 the end of a username, instead of just "/". Bash sets this to
115 char **tilde_additional_suffixes
= (char **)default_suffixes
;
117 /* Find the start of a tilde expansion in STRING, and return the index of
118 the tilde which starts the expansion. Place the length of the text
119 which identified this tilde starter in LEN, excluding the tilde itself. */
121 tilde_find_prefix (string
, len
)
125 register int i
, j
, string_len
;
126 register char **prefixes
;
128 prefixes
= tilde_additional_prefixes
;
130 string_len
= strlen (string
);
133 if (*string
== '\0' || *string
== '~')
138 for (i
= 0; i
< string_len
; i
++)
140 for (j
= 0; prefixes
[j
]; j
++)
142 if (strncmp (string
+ i
, prefixes
[j
], strlen (prefixes
[j
])) == 0)
144 *len
= strlen (prefixes
[j
]) - 1;
153 /* Find the end of a tilde expansion in STRING, and return the index of
154 the character which ends the tilde definition. */
156 tilde_find_suffix (string
)
159 register int i
, j
, string_len
;
160 register char **suffixes
;
162 suffixes
= tilde_additional_suffixes
;
163 string_len
= strlen (string
);
165 for (i
= 0; i
< string_len
; i
++)
167 #if defined (__MSDOS__)
168 if (string
[i
] == '/' || string
[i
] == '\\' /* || !string[i] */)
170 if (string
[i
] == '/' /* || !string[i] */)
174 for (j
= 0; suffixes
&& suffixes
[j
]; j
++)
176 if (strncmp (string
+ i
, suffixes
[j
], strlen (suffixes
[j
])) == 0)
183 /* Return a new string which is the result of tilde expanding STRING. */
185 tilde_expand (string
)
189 int result_size
, result_index
;
191 result_index
= result_size
= 0;
192 if (result
= strchr (string
, '~'))
193 result
= xmalloc (result_size
= (strlen (string
) + 16));
195 result
= xmalloc (result_size
= (strlen (string
) + 1));
197 /* Scan through STRING expanding tildes as we come to them. */
200 register int start
, end
;
201 char *tilde_word
, *expansion
;
204 /* Make START point to the tilde which starts the expansion. */
205 start
= tilde_find_prefix (string
, &len
);
207 /* Copy the skipped text into the result. */
208 if ((result_index
+ start
+ 1) > result_size
)
209 result
= xrealloc (result
, 1 + (result_size
+= (start
+ 20)));
211 strncpy (result
+ result_index
, string
, start
);
212 result_index
+= start
;
214 /* Advance STRING to the starting tilde. */
217 /* Make END be the index of one after the last character of the
219 end
= tilde_find_suffix (string
);
221 /* If both START and END are zero, we are all done. */
225 /* Expand the entire tilde word, and copy it into RESULT. */
226 tilde_word
= xmalloc (1 + end
);
227 strncpy (tilde_word
, string
, end
);
228 tilde_word
[end
] = '\0';
231 expansion
= tilde_expand_word (tilde_word
);
234 len
= strlen (expansion
);
236 /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
237 $HOME for `user' is /. On cygwin, // denotes a network drive. */
238 if (len
> 1 || *expansion
!= '/' || *string
!= '/')
241 if ((result_index
+ len
+ 1) > result_size
)
242 result
= xrealloc (result
, 1 + (result_size
+= (len
+ 20)));
244 strcpy (result
+ result_index
, expansion
);
250 result
[result_index
] = '\0';
255 /* Take FNAME and return the tilde prefix we want expanded. If LENP is
256 non-null, the index of the end of the prefix into FNAME is returned in
257 the location it points to. */
259 isolate_tilde_prefix (fname
, lenp
)
266 ret
= xmalloc (strlen (fname
));
267 #if defined (__MSDOS__)
268 for (i
= 1; fname
[i
] && fname
[i
] != '/' && fname
[i
] != '\\'; i
++)
270 for (i
= 1; fname
[i
] && fname
[i
] != '/'; i
++)
272 ret
[i
- 1] = fname
[i
];
279 /* Return a string that is PREFIX concatenated with SUFFIX starting at
282 glue_prefix_and_suffix (prefix
, suffix
, suffind
)
283 char *prefix
, *suffix
;
289 plen
= (prefix
&& *prefix
) ? strlen (prefix
) : 0;
290 slen
= strlen (suffix
+ suffind
);
291 ret
= xmalloc (plen
+ slen
+ 1);
293 strcpy (ret
, prefix
);
294 strcpy (ret
+ plen
, suffix
+ suffind
);
298 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
299 tilde. If there is no expansion, call tilde_expansion_failure_hook.
300 This always returns a newly-allocated string, never static storage. */
302 tilde_expand_word (filename
)
303 const char *filename
;
305 char *dirname
, *expansion
, *username
;
307 struct passwd
*user_entry
;
310 return ((char *)NULL
);
312 if (*filename
!= '~')
313 return (savestring (filename
));
315 /* A leading `~/' or a bare `~' is *always* translated to the value of
316 $HOME or the home directory of the current user, regardless of any
317 preexpansion hook. */
318 if (filename
[1] == '\0' || filename
[1] == '/')
320 /* Prefix $HOME to the rest of the string. */
321 expansion
= sh_get_env_value ("HOME");
323 /* If there is no HOME variable, look up the directory in
324 the password database. */
326 expansion
= sh_get_home_dir ();
328 return (glue_prefix_and_suffix (expansion
, filename
, 1));
331 username
= isolate_tilde_prefix (filename
, &user_len
);
333 if (tilde_expansion_preexpansion_hook
)
335 expansion
= (*tilde_expansion_preexpansion_hook
) (username
);
338 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
345 /* No preexpansion hook, or the preexpansion hook failed. Look in the
346 password database. */
347 dirname
= (char *)NULL
;
348 user_entry
= getpwnam (username
);
351 /* If the calling program has a special syntax for expanding tildes,
352 and we couldn't find a standard expansion, then let them try. */
353 if (tilde_expansion_failure_hook
)
355 expansion
= (*tilde_expansion_failure_hook
) (username
);
358 dirname
= glue_prefix_and_suffix (expansion
, filename
, user_len
);
363 /* If we don't have a failure hook, or if the failure hook did not
364 expand the tilde, return a copy of what we were passed. */
366 dirname
= savestring (filename
);
371 dirname
= glue_prefix_and_suffix (user_entry
->pw_dir
, filename
, user_len
);
387 char *result
, line
[512];
392 printf ("~expand: ");
396 strcpy (line
, "done");
398 if ((strcmp (line
, "done") == 0) ||
399 (strcmp (line
, "quit") == 0) ||
400 (strcmp (line
, "exit") == 0))
406 result
= tilde_expand (line
);
407 printf (" --> %s\n", result
);
413 static void memory_error_and_abort ();
419 char *temp
= (char *)malloc (bytes
);
422 memory_error_and_abort ();
427 xrealloc (pointer
, bytes
)
434 temp
= (char *)malloc (bytes
);
436 temp
= (char *)realloc (pointer
, bytes
);
439 memory_error_and_abort ();
445 memory_error_and_abort ()
447 fprintf (stderr
, "readline: out of virtual memory\n");
453 * compile-command: "gcc -g -DTEST -o tilde tilde.c"