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