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