7f33db4db96bf8a6dee73a90d8332f58fd845b7e
[platform/upstream/bash.git] / lib / readline / tilde.c
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 extern char *xmalloc __P((int));
54 extern char *xrealloc __P((void *, int));
55 #endif /* TEST || STATIC_MALLOC */
56
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 */
61
62 #if !defined (savestring)
63 #  ifndef strcpy
64 extern char *strcpy ();
65 #  endif
66 #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
67 #endif /* !savestring */
68
69 #if !defined (NULL)
70 #  if defined (__STDC__)
71 #    define NULL ((void *) 0)
72 #  else
73 #    define NULL 0x0
74 #  endif /* !__STDC__ */
75 #endif /* !NULL */
76
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 *));
82
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 };
88
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 };
94
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;
100
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;
106
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
109    `=~' and `:~'. */
110 char **tilde_additional_prefixes = (char **)default_prefixes;
111
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
114    `:' and `=~'. */
115 char **tilde_additional_suffixes = (char **)default_suffixes;
116
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. */
120 static int
121 tilde_find_prefix (string, len)
122      char *string;
123      int *len;
124 {
125   register int i, j, string_len;
126   register char **prefixes;
127
128   prefixes = tilde_additional_prefixes;
129
130   string_len = strlen (string);
131   *len = 0;
132
133   if (*string == '\0' || *string == '~')
134     return (0);
135
136   if (prefixes)
137     {
138       for (i = 0; i < string_len; i++)
139         {
140           for (j = 0; prefixes[j]; j++)
141             {
142               if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
143                 {
144                   *len = strlen (prefixes[j]) - 1;
145                   return (i + *len);
146                 }
147             }
148         }
149     }
150   return (string_len);
151 }
152
153 /* Find the end of a tilde expansion in STRING, and return the index of
154    the character which ends the tilde definition.  */
155 static int
156 tilde_find_suffix (string)
157      char *string;
158 {
159   register int i, j, string_len;
160   register char **suffixes;
161
162   suffixes = tilde_additional_suffixes;
163   string_len = strlen (string);
164
165   for (i = 0; i < string_len; i++)
166     {
167 #if defined (__MSDOS__)
168       if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
169 #else
170       if (string[i] == '/' /* || !string[i] */)
171 #endif
172         break;
173
174       for (j = 0; suffixes && suffixes[j]; j++)
175         {
176           if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
177             return (i);
178         }
179     }
180   return (i);
181 }
182
183 /* Return a new string which is the result of tilde expanding STRING. */
184 char *
185 tilde_expand (string)
186      const char *string;
187 {
188   char *result;
189   int result_size, result_index;
190
191   result_index = result_size = 0;
192   if (result = strchr (string, '~'))
193     result = xmalloc (result_size = (strlen (string) + 16));
194   else
195     result = xmalloc (result_size = (strlen (string) + 1));
196
197   /* Scan through STRING expanding tildes as we come to them. */
198   while (1)
199     {
200       register int start, end;
201       char *tilde_word, *expansion;
202       int len;
203
204       /* Make START point to the tilde which starts the expansion. */
205       start = tilde_find_prefix (string, &len);
206
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)));
210
211       strncpy (result + result_index, string, start);
212       result_index += start;
213
214       /* Advance STRING to the starting tilde. */
215       string += start;
216
217       /* Make END be the index of one after the last character of the
218          username. */
219       end = tilde_find_suffix (string);
220
221       /* If both START and END are zero, we are all done. */
222       if (!start && !end)
223         break;
224
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';
229       string += end;
230
231       expansion = tilde_expand_word (tilde_word);
232       free (tilde_word);
233
234       len = strlen (expansion);
235 #ifdef __CYGWIN__
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 != '/')
239 #endif
240         {
241           if ((result_index + len + 1) > result_size)
242             result = xrealloc (result, 1 + (result_size += (len + 20)));
243
244           strcpy (result + result_index, expansion);
245           result_index += len;
246         }
247       free (expansion);
248     }
249
250   result[result_index] = '\0';
251
252   return (result);
253 }
254
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. */
258 static char *
259 isolate_tilde_prefix (fname, lenp)
260      char *fname;
261      int *lenp;
262 {
263   char *ret;
264   int i;
265
266   ret = xmalloc (strlen (fname));
267 #if defined (__MSDOS__)
268   for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
269 #else
270   for (i = 1; fname[i] && fname[i] != '/'; i++)
271 #endif
272     ret[i - 1] = fname[i];
273   ret[i - 1] = '\0';
274   if (lenp)
275     *lenp = i;
276   return ret;
277 }
278
279 /* Return a string that is PREFIX concatenated with SUFFIX starting at
280    SUFFIND. */
281 static char *
282 glue_prefix_and_suffix (prefix, suffix, suffind)
283      char *prefix, *suffix;
284      int suffind;
285 {
286   char *ret;
287   int plen, slen;
288
289   plen = (prefix && *prefix) ? strlen (prefix) : 0;
290   slen = strlen (suffix + suffind);
291   ret = xmalloc (plen + slen + 1);
292   if (plen)
293     strcpy (ret, prefix);
294   strcpy (ret + plen, suffix + suffind);
295   return ret;
296 }
297
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. */
301 char *
302 tilde_expand_word (filename)
303      const char *filename;
304 {
305   char *dirname, *expansion, *username;
306   int user_len;
307   struct passwd *user_entry;
308
309   if (filename == 0)
310     return ((char *)NULL);
311
312   if (*filename != '~')
313     return (savestring (filename));
314
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] == '/')
319     {
320       /* Prefix $HOME to the rest of the string. */
321       expansion = sh_get_env_value ("HOME");
322
323       /* If there is no HOME variable, look up the directory in
324          the password database. */
325       if (expansion == 0)
326         expansion = sh_get_home_dir ();
327
328       return (glue_prefix_and_suffix (expansion, filename, 1));
329     }
330
331   username = isolate_tilde_prefix (filename, &user_len);
332
333   if (tilde_expansion_preexpansion_hook)
334     {
335       expansion = (*tilde_expansion_preexpansion_hook) (username);
336       if (expansion)
337         {
338           dirname = glue_prefix_and_suffix (expansion, filename, user_len);
339           free (username);
340           free (expansion);
341           return (dirname);
342         }
343     }
344
345   /* No preexpansion hook, or the preexpansion hook failed.  Look in the
346      password database. */
347   dirname = (char *)NULL;
348   user_entry = getpwnam (username);
349   if (user_entry == 0)
350     {
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)
354         {
355           expansion = (*tilde_expansion_failure_hook) (username);
356           if (expansion)
357             {
358               dirname = glue_prefix_and_suffix (expansion, filename, user_len);
359               free (expansion);
360             }
361         }
362       free (username);
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. */
365       if (dirname == 0)
366         dirname = savestring (filename);
367     }
368   else
369     {
370       free (username);
371       dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
372     }
373
374   endpwent ();
375   return (dirname);
376 }
377
378 \f
379 #if defined (TEST)
380 #undef NULL
381 #include <stdio.h>
382
383 main (argc, argv)
384      int argc;
385      char **argv;
386 {
387   char *result, line[512];
388   int done = 0;
389
390   while (!done)
391     {
392       printf ("~expand: ");
393       fflush (stdout);
394
395       if (!gets (line))
396         strcpy (line, "done");
397
398       if ((strcmp (line, "done") == 0) ||
399           (strcmp (line, "quit") == 0) ||
400           (strcmp (line, "exit") == 0))
401         {
402           done = 1;
403           break;
404         }
405
406       result = tilde_expand (line);
407       printf ("  --> %s\n", result);
408       free (result);
409     }
410   exit (0);
411 }
412
413 static void memory_error_and_abort ();
414
415 static char *
416 xmalloc (bytes)
417      int bytes;
418 {
419   char *temp = (char *)malloc (bytes);
420
421   if (!temp)
422     memory_error_and_abort ();
423   return (temp);
424 }
425
426 static char *
427 xrealloc (pointer, bytes)
428      char *pointer;
429      int bytes;
430 {
431   char *temp;
432
433   if (!pointer)
434     temp = (char *)malloc (bytes);
435   else
436     temp = (char *)realloc (pointer, bytes);
437
438   if (!temp)
439     memory_error_and_abort ();
440
441   return (temp);
442 }
443
444 static void
445 memory_error_and_abort ()
446 {
447   fprintf (stderr, "readline: out of virtual memory\n");
448   abort ();
449 }
450
451 /*
452  * Local variables:
453  * compile-command: "gcc -g -DTEST -o tilde tilde.c"
454  * end:
455  */
456 #endif /* TEST */