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