da75d957863d9d48e00d39ee12a40a0549d04b52
[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_STRING_H)
23 #  include <string.h>
24 #else /* !HAVE_STRING_H */
25 #  include <strings.h>
26 #endif /* !HAVE_STRING_H */  
27
28 #if defined (HAVE_STDLIB_H)
29 #  include <stdlib.h>
30 #else
31 #  include "ansi_stdlib.h"
32 #endif /* HAVE_STDLIB_H */
33
34 #include "tilde.h"
35 #include <sys/types.h>
36 #include <pwd.h>
37
38 #if defined (USG) && !defined (HAVE_GETPW_DECLS)
39 extern struct passwd *getpwuid (), *getpwnam ();
40 #endif /* USG && !defined (HAVE_GETPW_DECLS) */
41
42 #if !defined (savestring)
43 extern char *xmalloc ();
44 #  ifndef strcpy
45 extern char *strcpy ();
46 #  endif
47 #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
48 #endif /* !savestring */
49
50 #if !defined (NULL)
51 #  if defined (__STDC__)
52 #    define NULL ((void *) 0)
53 #  else
54 #    define NULL 0x0
55 #  endif /* !__STDC__ */
56 #endif /* !NULL */
57
58 #if defined (TEST) || defined (STATIC_MALLOC)
59 static char *xmalloc (), *xrealloc ();
60 #else
61 extern char *xmalloc (), *xrealloc ();
62 #endif /* TEST || STATIC_MALLOC */
63
64 /* The default value of tilde_additional_prefixes.  This is set to
65    whitespace preceding a tilde so that simple programs which do not
66    perform any word separation get desired behaviour. */
67 static char *default_prefixes[] =
68   { " ~", "\t~", (char *)NULL };
69
70 /* The default value of tilde_additional_suffixes.  This is set to
71    whitespace or newline so that simple programs which do not
72    perform any word separation get desired behaviour. */
73 static char *default_suffixes[] =
74   { " ", "\n", (char *)NULL };
75
76 /* If non-null, this contains the address of a function to call if the
77    standard meaning for expanding a tilde fails.  The function is called
78    with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
79    which is the expansion, or a NULL pointer if there is no expansion. */
80 CPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL;
81
82 /* When non-null, this is a NULL terminated array of strings which
83    are duplicates for a tilde prefix.  Bash uses this to expand
84    `=~' and `:~'. */
85 char **tilde_additional_prefixes = default_prefixes;
86
87 /* When non-null, this is a NULL terminated array of strings which match
88    the end of a username, instead of just "/".  Bash sets this to
89    `:' and `=~'. */
90 char **tilde_additional_suffixes = default_suffixes;
91
92 /* Find the start of a tilde expansion in STRING, and return the index of
93    the tilde which starts the expansion.  Place the length of the text
94    which identified this tilde starter in LEN, excluding the tilde itself. */
95 static int
96 tilde_find_prefix (string, len)
97      char *string;
98      int *len;
99 {
100   register int i, j, string_len;
101   register char **prefixes = tilde_additional_prefixes;
102
103   string_len = strlen (string);
104   *len = 0;
105
106   if (!*string || *string == '~')
107     return (0);
108
109   if (prefixes)
110     {
111       for (i = 0; i < string_len; i++)
112         {
113           for (j = 0; prefixes[j]; j++)
114             {
115               if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
116                 {
117                   *len = strlen (prefixes[j]) - 1;
118                   return (i + *len);
119                 }
120             }
121         }
122     }
123   return (string_len);
124 }
125
126 /* Find the end of a tilde expansion in STRING, and return the index of
127    the character which ends the tilde definition.  */
128 static int
129 tilde_find_suffix (string)
130      char *string;
131 {
132   register int i, j, string_len;
133   register char **suffixes = tilde_additional_suffixes;
134
135   string_len = strlen (string);
136
137   for (i = 0; i < string_len; i++)
138     {
139       if (string[i] == '/' || !string[i])
140         break;
141
142       for (j = 0; suffixes && suffixes[j]; j++)
143         {
144           if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
145             return (i);
146         }
147     }
148   return (i);
149 }
150
151 /* Return a new string which is the result of tilde expanding STRING. */
152 char *
153 tilde_expand (string)
154      char *string;
155 {
156   char *result, *tilde_expand_word ();
157   int result_size, result_index;
158
159   result_size = result_index = 0;
160   result = (char *)NULL;
161
162   /* Scan through STRING expanding tildes as we come to them. */
163   while (1)
164     {
165       register int start, end;
166       char *tilde_word, *expansion;
167       int len;
168
169       /* Make START point to the tilde which starts the expansion. */
170       start = tilde_find_prefix (string, &len);
171
172       /* Copy the skipped text into the result. */
173       if ((result_index + start + 1) > result_size)
174         result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
175
176       strncpy (result + result_index, string, start);
177       result_index += start;
178
179       /* Advance STRING to the starting tilde. */
180       string += start;
181
182       /* Make END be the index of one after the last character of the
183          username. */
184       end = tilde_find_suffix (string);
185
186       /* If both START and END are zero, we are all done. */
187       if (!start && !end)
188         break;
189
190       /* Expand the entire tilde word, and copy it into RESULT. */
191       tilde_word = (char *)xmalloc (1 + end);
192       strncpy (tilde_word, string, end);
193       tilde_word[end] = '\0';
194       string += end;
195
196       expansion = tilde_expand_word (tilde_word);
197       free (tilde_word);
198
199       len = strlen (expansion);
200       if ((result_index + len + 1) > result_size)
201         result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
202
203       strcpy (result + result_index, expansion);
204       result_index += len;
205       free (expansion);
206     }
207
208   result[result_index] = '\0';
209
210   return (result);
211 }
212
213 /* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
214    tilde.  If there is no expansion, call tilde_expansion_failure_hook. */
215 char *
216 tilde_expand_word (filename)
217      char *filename;
218 {
219   char *dirname;
220
221   dirname = filename ? savestring (filename) : (char *)NULL;
222
223   if (dirname && *dirname == '~')
224     {
225       char *temp_name;
226       if (!dirname[1] || dirname[1] == '/')
227         {
228           /* Prepend $HOME to the rest of the string. */
229           char *temp_home = (char *)getenv ("HOME");
230
231           /* If there is no HOME variable, look up the directory in
232              the password database. */
233           if (!temp_home)
234             {
235               struct passwd *entry;
236
237               entry = getpwuid (getuid ());
238               if (entry)
239                 temp_home = entry->pw_dir;
240             }
241
242           temp_name = xmalloc (1 + strlen (&dirname[1])
243                                  + (temp_home ? strlen (temp_home) : 0));
244           temp_name[0] = '\0';
245           if (temp_home)
246             strcpy (temp_name, temp_home);
247           strcat (temp_name, dirname + 1);
248           free (dirname);
249           dirname = temp_name;
250         }
251       else
252         {
253           char *username;
254           struct passwd *user_entry;
255           int i;
256
257           username = xmalloc (strlen (dirname));
258           for (i = 1; dirname[i] && dirname[i] != '/'; i++)
259             username[i - 1] = dirname[i];
260           username[i - 1] = '\0';
261
262           if ((user_entry = getpwnam (username)) == 0)
263             {
264               /* If the calling program has a special syntax for
265                  expanding tildes, and we couldn't find a standard
266                  expansion, then let them try. */
267               if (tilde_expansion_failure_hook)
268                 {
269                   char *expansion;
270
271                   expansion = (*tilde_expansion_failure_hook) (username);
272
273                   if (expansion)
274                     {
275                       temp_name = xmalloc (1 + strlen (expansion)
276                                                   + strlen (&dirname[i]));
277                       strcpy (temp_name, expansion);
278                       strcat (temp_name, &dirname[i]);
279                       free (expansion);
280                       free (dirname);
281                       dirname = temp_name;
282                     }
283                 }
284               /* We shouldn't report errors. */
285             }
286           else
287             {
288               temp_name = xmalloc (1 + strlen (user_entry->pw_dir)
289                                      + strlen (&dirname[i]));
290               strcpy (temp_name, user_entry->pw_dir);
291               strcat (temp_name, &dirname[i]);
292               free (dirname);
293               dirname = temp_name;
294             }
295           endpwent ();
296           free (username);
297         }
298     }
299   return (dirname);
300 }
301
302 \f
303 #if defined (TEST)
304 #undef NULL
305 #include <stdio.h>
306
307 main (argc, argv)
308      int argc;
309      char **argv;
310 {
311   char *result, line[512];
312   int done = 0;
313
314   while (!done)
315     {
316       printf ("~expand: ");
317       fflush (stdout);
318
319       if (!gets (line))
320         strcpy (line, "done");
321
322       if ((strcmp (line, "done") == 0) ||
323           (strcmp (line, "quit") == 0) ||
324           (strcmp (line, "exit") == 0))
325         {
326           done = 1;
327           break;
328         }
329
330       result = tilde_expand (line);
331       printf ("  --> %s\n", result);
332       free (result);
333     }
334   exit (0);
335 }
336
337 static void memory_error_and_abort ();
338
339 static char *
340 xmalloc (bytes)
341      int bytes;
342 {
343   char *temp = (char *)malloc (bytes);
344
345   if (!temp)
346     memory_error_and_abort ();
347   return (temp);
348 }
349
350 static char *
351 xrealloc (pointer, bytes)
352      char *pointer;
353      int bytes;
354 {
355   char *temp;
356
357   if (!pointer)
358     temp = (char *)malloc (bytes);
359   else
360     temp = (char *)realloc (pointer, bytes);
361
362   if (!temp)
363     memory_error_and_abort ();
364
365   return (temp);
366 }
367
368 static void
369 memory_error_and_abort ()
370 {
371   fprintf (stderr, "readline: Out of virtual memory!\n");
372   abort ();
373 }
374
375 /*
376  * Local variables:
377  * compile-command: "gcc -g -DTEST -o tilde tilde.c"
378  * end:
379  */
380 #endif /* TEST */