source: trunk/third/readline/tilde.c @ 12992

Revision 12992, 11.2 KB checked in by kcr, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12991, which included commits to RCS files with non-trunk default branches.
Line 
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)
51extern struct passwd *getpwuid (), *getpwnam ();
52#endif /* !HAVE_GETPW_DECLS */
53
54#if !defined (savestring)
55extern char *xmalloc ();
56#  ifndef strcpy
57extern 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)
71static char *xmalloc (), *xrealloc ();
72#else
73extern 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. */
79extern char *get_home_dir ();
80extern 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. */
85static 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. */
91static 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. */
98CPFunction *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. */
104CPFunction *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 `:~'. */
109char **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 `=~'. */
114char **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. */
119static int
120tilde_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.  */
152static int
153tilde_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. */
177char *
178tilde_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. */
244static char *
245isolate_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. */
263static char *
264glue_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. */
283char *
284tilde_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
361#if defined (TEST)
362#undef NULL
363#include <stdio.h>
364
365main (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
395static void memory_error_and_abort ();
396
397static char *
398xmalloc (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
408static char *
409xrealloc (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
426static void
427memory_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 */
Note: See TracBrowser for help on using the repository browser.