Imported from ../bash-2.05a.tar.gz.
[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 void *xmalloc (), *xrealloc ();
52 #else
53 #  include "xmalloc.h"
54 #endif /* TEST || STATIC_MALLOC */
55
56 #if !defined (HAVE_GETPW_DECLS)
57 extern struct passwd *getpwuid PARAMS((uid_t));
58 extern struct passwd *getpwnam PARAMS((const char *));
59 #endif /* !HAVE_GETPW_DECLS */
60
61 #if !defined (savestring)
62 #  ifndef strcpy
63 extern char *strcpy ();
64 #  endif
65 #define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
66 #endif /* !savestring */
67
68 #if !defined (NULL)
69 #  if defined (__STDC__)
70 #    define NULL ((void *) 0)
71 #  else
72 #    define NULL 0x0
73 #  endif /* !__STDC__ */
74 #endif /* !NULL */
75
76 /* If being compiled as part of bash, these will be satisfied from
77    variables.o.  If being compiled as part of readline, they will
78    be satisfied from shell.o. */
79 extern char *sh_get_home_dir PARAMS((void));
80 extern char *sh_get_env_value PARAMS((const char *));
81
82 /* The default value of tilde_additional_prefixes.  This is set to
83    whitespace preceding a tilde so that simple programs which do not
84    perform any word separation get desired behaviour. */
85 static const char *default_prefixes[] =
86   { " ~", "\t~", (const char *)NULL };
87
88 /* The default value of tilde_additional_suffixes.  This is set to
89    whitespace or newline so that simple programs which do not
90    perform any word separation get desired behaviour. */
91 static const char *default_suffixes[] =
92   { " ", "\n", (const char *)NULL };
93
94 /* If non-null, this contains the address of a function that the application
95    wants called before trying the standard tilde expansions.  The function
96    is called with the text sans tilde, and returns a malloc()'ed string
97    which is the expansion, or a NULL pointer if the expansion fails. */
98 tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
99
100 /* If non-null, this contains the address of a function to call if the
101    standard meaning for expanding a tilde fails.  The function is called
102    with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
103    which is the expansion, or a NULL pointer if there is no expansion. */
104 tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
105
106 /* When non-null, this is a NULL terminated array of strings which
107    are duplicates for a tilde prefix.  Bash uses this to expand
108    `=~' and `:~'. */
109 char **tilde_additional_prefixes = (char **)default_prefixes;
110
111 /* When non-null, this is a NULL terminated array of strings which match
112    the end of a username, instead of just "/".  Bash sets this to
113    `:' and `=~'. */
114 char **tilde_additional_suffixes = (char **)default_suffixes;
115
116 static int tilde_find_prefix PARAMS((const char *, int *));
117 static int tilde_find_suffix PARAMS((const char *));
118 static char *isolate_tilde_prefix PARAMS((const char *, int *));
119 static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
120
121 /* Find the start of a tilde expansion in STRING, and return the index of
122    the tilde which starts the expansion.  Place the length of the text
123    which identified this tilde starter in LEN, excluding the tilde itself. */
124 static int
125 tilde_find_prefix (string, len)
126      const char *string;
127      int *len;
128 {
129   register int i, j, string_len;
130   register char **prefixes;
131
132   prefixes = tilde_additional_prefixes;
133
134   string_len = strlen (string);
135   *len = 0;
136
137   if (*string == '\0' || *string == '~')
138     return (0);
139
140   if (prefixes)
141     {
142       for (i = 0; i < string_len; i++)
143         {
144           for (j = 0; prefixes[j]; j++)
145             {
146               if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
147                 {
148                   *len = strlen (prefixes[j]) - 1;
149                   return (i + *len);
150                 }
151             }
152         }
153     }
154   return (string_len);
155 }
156
157 /* Find the end of a tilde expansion in STRING, and return the index of
158    the character which ends the tilde definition.  */
159 static int
160 tilde_find_suffix (string)
161      const char *string;
162 {
163   register int i, j, string_len;
164   register char **suffixes;
165
166   suffixes = tilde_additional_suffixes;
167   string_len = strlen (string);
168
169   for (i = 0; i < string_len; i++)
170     {
171 #if defined (__MSDOS__)
172       if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
173 #else
174       if (string[i] == '/' /* || !string[i] */)
175 #endif
176         break;
177
178       for (j = 0; suffixes && suffixes[j]; j++)
179         {
180           if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
181             return (i);
182         }
183     }
184   return (i);
185 }
186
187 /* Return a new string which is the result of tilde expanding STRING. */
188 char *
189 tilde_expand (string)
190      const char *string;
191 {
192   char *result;
193   int result_size, result_index;
194
195   result_index = result_size = 0;
196   if (result = strchr (string, '~'))
197     result = (char *)xmalloc (result_size = (strlen (string) + 16));
198   else
199     result = (char *)xmalloc (result_size = (strlen (string) + 1));
200
201   /* Scan through STRING expanding tildes as we come to them. */
202   while (1)
203     {
204       register int start, end;
205       char *tilde_word, *expansion;
206       int len;
207
208       /* Make START point to the tilde which starts the expansion. */
209       start = tilde_find_prefix (string, &len);
210
211       /* Copy the skipped text into the result. */
212       if ((result_index + start + 1) > result_size)
213         result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
214
215       strncpy (result + result_index, string, start);
216       result_index += start;
217
218       /* Advance STRING to the starting tilde. */
219       string += start;
220
221       /* Make END be the index of one after the last character of the
222          username. */
223       end = tilde_find_suffix (string);
224
225       /* If both START and END are zero, we are all done. */
226       if (!start && !end)
227         break;
228
229       /* Expand the entire tilde word, and copy it into RESULT. */
230       tilde_word = (char *)xmalloc (1 + end);
231       strncpy (tilde_word, string, end);
232       tilde_word[end] = '\0';
233       string += end;
234
235       expansion = tilde_expand_word (tilde_word);
236       free (tilde_word);
237
238       len = strlen (expansion);
239 #ifdef __CYGWIN__
240       /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
241          $HOME for `user' is /.  On cygwin, // denotes a network drive. */
242       if (len > 1 || *expansion != '/' || *string != '/')
243 #endif
244         {
245           if ((result_index + len + 1) > result_size)
246             result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
247
248           strcpy (result + result_index, expansion);
249           result_index += len;
250         }
251       free (expansion);
252     }
253
254   result[result_index] = '\0';
255
256   return (result);
257 }
258
259 /* Take FNAME and return the tilde prefix we want expanded.  If LENP is
260    non-null, the index of the end of the prefix into FNAME is returned in
261    the location it points to. */
262 static char *
263 isolate_tilde_prefix (fname, lenp)
264      const char *fname;
265      int *lenp;
266 {
267   char *ret;
268   int i;
269
270   ret = (char *)xmalloc (strlen (fname));
271 #if defined (__MSDOS__)
272   for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
273 #else
274   for (i = 1; fname[i] && fname[i] != '/'; i++)
275 #endif
276     ret[i - 1] = fname[i];
277   ret[i - 1] = '\0';
278   if (lenp)
279     *lenp = i;
280   return ret;
281 }
282
283 /* Return a string that is PREFIX concatenated with SUFFIX starting at
284    SUFFIND. */
285 static char *
286 glue_prefix_and_suffix (prefix, suffix, suffind)
287      char *prefix;
288      const char *suffix;
289      int suffind;
290 {
291   char *ret;
292   int plen, slen;
293
294   plen = (prefix && *prefix) ? strlen (prefix) : 0;
295   slen = strlen (suffix + suffind);
296   ret = (char *)xmalloc (plen + slen + 1);
297   if (plen)
298     strcpy (ret, prefix);
299   strcpy (ret + plen, suffix + suffind);
300   return ret;
301 }
302
303 /* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
304    tilde.  If there is no expansion, call tilde_expansion_failure_hook.
305    This always returns a newly-allocated string, never static storage. */
306 char *
307 tilde_expand_word (filename)
308      const char *filename;
309 {
310   char *dirname, *expansion, *username;
311   int user_len;
312   struct passwd *user_entry;
313
314   if (filename == 0)
315     return ((char *)NULL);
316
317   if (*filename != '~')
318     return (savestring (filename));
319
320   /* A leading `~/' or a bare `~' is *always* translated to the value of
321      $HOME or the home directory of the current user, regardless of any
322      preexpansion hook. */
323   if (filename[1] == '\0' || filename[1] == '/')
324     {
325       /* Prefix $HOME to the rest of the string. */
326       expansion = sh_get_env_value ("HOME");
327
328       /* If there is no HOME variable, look up the directory in
329          the password database. */
330       if (expansion == 0)
331         expansion = sh_get_home_dir ();
332
333       return (glue_prefix_and_suffix (expansion, filename, 1));
334     }
335
336   username = isolate_tilde_prefix (filename, &user_len);
337
338   if (tilde_expansion_preexpansion_hook)
339     {
340       expansion = (*tilde_expansion_preexpansion_hook) (username);
341       if (expansion)
342         {
343           dirname = glue_prefix_and_suffix (expansion, filename, user_len);
344           free (username);
345           free (expansion);
346           return (dirname);
347         }
348     }
349
350   /* No preexpansion hook, or the preexpansion hook failed.  Look in the
351      password database. */
352   dirname = (char *)NULL;
353   user_entry = getpwnam (username);
354   if (user_entry == 0)
355     {
356       /* If the calling program has a special syntax for expanding tildes,
357          and we couldn't find a standard expansion, then let them try. */
358       if (tilde_expansion_failure_hook)
359         {
360           expansion = (*tilde_expansion_failure_hook) (username);
361           if (expansion)
362             {
363               dirname = glue_prefix_and_suffix (expansion, filename, user_len);
364               free (expansion);
365             }
366         }
367       free (username);
368       /* If we don't have a failure hook, or if the failure hook did not
369          expand the tilde, return a copy of what we were passed. */
370       if (dirname == 0)
371         dirname = savestring (filename);
372     }
373   else
374     {
375       free (username);
376       dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
377     }
378
379   endpwent ();
380   return (dirname);
381 }
382
383 \f
384 #if defined (TEST)
385 #undef NULL
386 #include <stdio.h>
387
388 main (argc, argv)
389      int argc;
390      char **argv;
391 {
392   char *result, line[512];
393   int done = 0;
394
395   while (!done)
396     {
397       printf ("~expand: ");
398       fflush (stdout);
399
400       if (!gets (line))
401         strcpy (line, "done");
402
403       if ((strcmp (line, "done") == 0) ||
404           (strcmp (line, "quit") == 0) ||
405           (strcmp (line, "exit") == 0))
406         {
407           done = 1;
408           break;
409         }
410
411       result = tilde_expand (line);
412       printf ("  --> %s\n", result);
413       free (result);
414     }
415   exit (0);
416 }
417
418 static void memory_error_and_abort ();
419
420 static void *
421 xmalloc (bytes)
422      size_t bytes;
423 {
424   void *temp = (char *)malloc (bytes);
425
426   if (!temp)
427     memory_error_and_abort ();
428   return (temp);
429 }
430
431 static void *
432 xrealloc (pointer, bytes)
433      void *pointer;
434      int bytes;
435 {
436   void *temp;
437
438   if (!pointer)
439     temp = malloc (bytes);
440   else
441     temp = realloc (pointer, bytes);
442
443   if (!temp)
444     memory_error_and_abort ();
445
446   return (temp);
447 }
448
449 static void
450 memory_error_and_abort ()
451 {
452   fprintf (stderr, "readline: out of virtual memory\n");
453   abort ();
454 }
455
456 /*
457  * Local variables:
458  * compile-command: "gcc -g -DTEST -o tilde tilde.c"
459  * end:
460  */
461 #endif /* TEST */