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