tilde.c revision 26497
121308Sache/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
221308Sache
321308Sache/* Copyright (C) 1988,1989 Free Software Foundation, Inc.
421308Sache
521308Sache   This file is part of GNU Readline, a library for reading lines
621308Sache   of text with interactive input and history editing.
721308Sache
821308Sache   Readline is free software; you can redistribute it and/or modify it
921308Sache   under the terms of the GNU General Public License as published by the
1021308Sache   Free Software Foundation; either version 1, or (at your option) any
1121308Sache   later version.
1221308Sache
1321308Sache   Readline is distributed in the hope that it will be useful, but
1421308Sache   WITHOUT ANY WARRANTY; without even the implied warranty of
1521308Sache   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1621308Sache   General Public License for more details.
1721308Sache
1821308Sache   You should have received a copy of the GNU General Public License
1921308Sache   along with Readline; see the file COPYING.  If not, write to the Free
2021308Sache   Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
2121308Sache
2221308Sache#if defined (HAVE_CONFIG_H)
2321308Sache#  include <config.h>
2421308Sache#endif
2521308Sache
2626497Sache#if defined (HAVE_UNISTD_H)
2726497Sache#  include <unistd.h>
2826497Sache#endif
2926497Sache
3021308Sache#if defined (HAVE_STRING_H)
3121308Sache#  include <string.h>
3221308Sache#else /* !HAVE_STRING_H */
3321308Sache#  include <strings.h>
3421308Sache#endif /* !HAVE_STRING_H */
3521308Sache
3621308Sache#if defined (HAVE_STDLIB_H)
3721308Sache#  include <stdlib.h>
3821308Sache#else
3921308Sache#  include "ansi_stdlib.h"
4021308Sache#endif /* HAVE_STDLIB_H */
4121308Sache
4221308Sache#include <sys/types.h>
4321308Sache#include <pwd.h>
4421308Sache
4521308Sache#include "tilde.h"
4621308Sache
4726497Sache#ifdef SHELL
4826497Sache#include "shell.h"
4926497Sache#endif
5026497Sache
5121308Sache#if !defined (HAVE_GETPW_DECLS)
5221308Sacheextern struct passwd *getpwuid (), *getpwnam ();
5321308Sache#endif /* !HAVE_GETPW_DECLS */
5421308Sache
5521308Sache#if !defined (savestring)
5621308Sacheextern char *xmalloc ();
5721308Sache#  ifndef strcpy
5821308Sacheextern char *strcpy ();
5921308Sache#  endif
6021308Sache#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
6121308Sache#endif /* !savestring */
6221308Sache
6321308Sache#if !defined (NULL)
6421308Sache#  if defined (__STDC__)
6521308Sache#    define NULL ((void *) 0)
6621308Sache#  else
6721308Sache#    define NULL 0x0
6821308Sache#  endif /* !__STDC__ */
6921308Sache#endif /* !NULL */
7021308Sache
7121308Sache#if defined (TEST) || defined (STATIC_MALLOC)
7221308Sachestatic char *xmalloc (), *xrealloc ();
7321308Sache#else
7421308Sacheextern char *xmalloc (), *xrealloc ();
7521308Sache#endif /* TEST || STATIC_MALLOC */
7621308Sache
7721308Sache/* The default value of tilde_additional_prefixes.  This is set to
7821308Sache   whitespace preceding a tilde so that simple programs which do not
7921308Sache   perform any word separation get desired behaviour. */
8021308Sachestatic char *default_prefixes[] =
8121308Sache  { " ~", "\t~", (char *)NULL };
8221308Sache
8321308Sache/* The default value of tilde_additional_suffixes.  This is set to
8421308Sache   whitespace or newline so that simple programs which do not
8521308Sache   perform any word separation get desired behaviour. */
8621308Sachestatic char *default_suffixes[] =
8721308Sache  { " ", "\n", (char *)NULL };
8821308Sache
8926497Sache/* If non-null, this contains the address of a function that the application
9026497Sache   wants called before trying the standard tilde expansions.  The function
9126497Sache   is called with the text sans tilde, and returns a malloc()'ed string
9226497Sache   which is the expansion, or a NULL pointer if the expansion fails. */
9326497SacheCPFunction *tilde_expansion_preexpansion_hook = (CPFunction *)NULL;
9426497Sache
9521308Sache/* If non-null, this contains the address of a function to call if the
9621308Sache   standard meaning for expanding a tilde fails.  The function is called
9721308Sache   with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
9821308Sache   which is the expansion, or a NULL pointer if there is no expansion. */
9921308SacheCPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL;
10021308Sache
10121308Sache/* When non-null, this is a NULL terminated array of strings which
10221308Sache   are duplicates for a tilde prefix.  Bash uses this to expand
10321308Sache   `=~' and `:~'. */
10421308Sachechar **tilde_additional_prefixes = default_prefixes;
10521308Sache
10621308Sache/* When non-null, this is a NULL terminated array of strings which match
10721308Sache   the end of a username, instead of just "/".  Bash sets this to
10821308Sache   `:' and `=~'. */
10921308Sachechar **tilde_additional_suffixes = default_suffixes;
11021308Sache
11121308Sache/* Find the start of a tilde expansion in STRING, and return the index of
11221308Sache   the tilde which starts the expansion.  Place the length of the text
11321308Sache   which identified this tilde starter in LEN, excluding the tilde itself. */
11421308Sachestatic int
11521308Sachetilde_find_prefix (string, len)
11621308Sache     char *string;
11721308Sache     int *len;
11821308Sache{
11921308Sache  register int i, j, string_len;
12021308Sache  register char **prefixes = tilde_additional_prefixes;
12121308Sache
12221308Sache  string_len = strlen (string);
12321308Sache  *len = 0;
12421308Sache
12526497Sache  if (*string == '\0' || *string == '~')
12621308Sache    return (0);
12721308Sache
12821308Sache  if (prefixes)
12921308Sache    {
13021308Sache      for (i = 0; i < string_len; i++)
13121308Sache	{
13221308Sache	  for (j = 0; prefixes[j]; j++)
13321308Sache	    {
13421308Sache	      if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
13521308Sache		{
13621308Sache		  *len = strlen (prefixes[j]) - 1;
13721308Sache		  return (i + *len);
13821308Sache		}
13921308Sache	    }
14021308Sache	}
14121308Sache    }
14221308Sache  return (string_len);
14321308Sache}
14421308Sache
14521308Sache/* Find the end of a tilde expansion in STRING, and return the index of
14621308Sache   the character which ends the tilde definition.  */
14721308Sachestatic int
14821308Sachetilde_find_suffix (string)
14921308Sache     char *string;
15021308Sache{
15121308Sache  register int i, j, string_len;
15226497Sache  register char **suffixes;
15321308Sache
15426497Sache  suffixes = tilde_additional_suffixes;
15521308Sache  string_len = strlen (string);
15621308Sache
15721308Sache  for (i = 0; i < string_len; i++)
15821308Sache    {
15926497Sache      if (string[i] == '/' /* || !string[i] */)
16021308Sache	break;
16121308Sache
16221308Sache      for (j = 0; suffixes && suffixes[j]; j++)
16321308Sache	{
16421308Sache	  if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
16521308Sache	    return (i);
16621308Sache	}
16721308Sache    }
16821308Sache  return (i);
16921308Sache}
17021308Sache
17126497Sache#if !defined (SHELL)
17226497Sachestatic char *
17326497Sacheget_string_value (varname)
17426497Sache     char *varname;
17526497Sache{
17626497Sache  return ((char *)getenv (varname));
17726497Sache}
17826497Sache#endif
17926497Sache
18021308Sache/* Return a new string which is the result of tilde expanding STRING. */
18121308Sachechar *
18221308Sachetilde_expand (string)
18321308Sache     char *string;
18421308Sache{
18526497Sache  char *result;
18621308Sache  int result_size, result_index;
18721308Sache
18826497Sache  result_index = result_size = 0;
18926497Sache  if (result = strchr (string, '~'))
19026497Sache    result = xmalloc (result_size = (strlen (string) + 16));
19126497Sache  else
19226497Sache    result = xmalloc (result_size = strlen (string));
19321308Sache
19421308Sache  /* Scan through STRING expanding tildes as we come to them. */
19521308Sache  while (1)
19621308Sache    {
19721308Sache      register int start, end;
19821308Sache      char *tilde_word, *expansion;
19921308Sache      int len;
20021308Sache
20121308Sache      /* Make START point to the tilde which starts the expansion. */
20221308Sache      start = tilde_find_prefix (string, &len);
20321308Sache
20421308Sache      /* Copy the skipped text into the result. */
20521308Sache      if ((result_index + start + 1) > result_size)
20621308Sache	result = xrealloc (result, 1 + (result_size += (start + 20)));
20721308Sache
20821308Sache      strncpy (result + result_index, string, start);
20921308Sache      result_index += start;
21021308Sache
21121308Sache      /* Advance STRING to the starting tilde. */
21221308Sache      string += start;
21321308Sache
21421308Sache      /* Make END be the index of one after the last character of the
21521308Sache	 username. */
21621308Sache      end = tilde_find_suffix (string);
21721308Sache
21821308Sache      /* If both START and END are zero, we are all done. */
21921308Sache      if (!start && !end)
22021308Sache	break;
22121308Sache
22221308Sache      /* Expand the entire tilde word, and copy it into RESULT. */
22321308Sache      tilde_word = xmalloc (1 + end);
22421308Sache      strncpy (tilde_word, string, end);
22521308Sache      tilde_word[end] = '\0';
22621308Sache      string += end;
22721308Sache
22821308Sache      expansion = tilde_expand_word (tilde_word);
22921308Sache      free (tilde_word);
23021308Sache
23121308Sache      len = strlen (expansion);
23221308Sache      if ((result_index + len + 1) > result_size)
23321308Sache	result = xrealloc (result, 1 + (result_size += (len + 20)));
23421308Sache
23521308Sache      strcpy (result + result_index, expansion);
23621308Sache      result_index += len;
23721308Sache      free (expansion);
23821308Sache    }
23921308Sache
24021308Sache  result[result_index] = '\0';
24121308Sache
24221308Sache  return (result);
24321308Sache}
24421308Sache
24526497Sache/* Take FNAME and return the tilde prefix we want expanded.  If LENP is
24626497Sache   non-null, the index of the end of the prefix into FNAME is returned in
24726497Sache   the location it points to. */
24826497Sachestatic char *
24926497Sacheisolate_tilde_prefix (fname, lenp)
25026497Sache     char *fname;
25126497Sache     int *lenp;
25226497Sache{
25326497Sache  char *ret;
25426497Sache  int i;
25526497Sache
25626497Sache  ret = xmalloc (strlen (fname));
25726497Sache  for (i = 1; fname[i] && fname[i] != '/'; i++)
25826497Sache    ret[i - 1] = fname[i];
25926497Sache  ret[i - 1] = '\0';
26026497Sache  if (lenp)
26126497Sache    *lenp = i;
26226497Sache  return ret;
26326497Sache}
26426497Sache
26526497Sache/* Return a string that is PREFIX concatenated with SUFFIX starting at
26626497Sache   SUFFIND. */
26726497Sachestatic char *
26826497Sacheglue_prefix_and_suffix (prefix, suffix, suffind)
26926497Sache     char *prefix, *suffix;
27026497Sache     int suffind;
27126497Sache{
27226497Sache  char *ret;
27326497Sache  int plen, slen;
27426497Sache
27526497Sache  plen = (prefix && *prefix) ? strlen (prefix) : 0;
27626497Sache  slen = strlen (suffix + suffind);
27726497Sache  ret = xmalloc (plen + slen + 1);
27826497Sache  if (prefix && *prefix)
27926497Sache    strcpy (ret, prefix);
28026497Sache  strcpy (ret + plen, suffix + suffind);
28126497Sache  return ret;
28226497Sache}
28326497Sache
28426497Sachestatic char *
28526497Sacheget_home_dir ()
28626497Sache{
28726497Sache  char *home_dir;
28826497Sache
28926497Sache#ifdef SHELL
29026497Sache  home_dir = (char *)NULL;
29126497Sache  if (current_user.home_dir == 0)
29226497Sache    get_current_user_info ();
29326497Sache  home_dir = current_user.home_dir;
29426497Sache#else
29526497Sache  struct passwd *entry;
29626497Sache
29726497Sache  home_dir = (char *)NULL;
29826497Sache  entry = getpwuid (getuid ());
29926497Sache  if (entry)
30026497Sache    home_dir = entry->pw_dir;
30126497Sache#endif
30226497Sache  return (home_dir);
30326497Sache}
30426497Sache
30521308Sache/* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
30626497Sache   tilde.  If there is no expansion, call tilde_expansion_failure_hook.
30726497Sache   This always returns a newly-allocated string, never static storage. */
30821308Sachechar *
30921308Sachetilde_expand_word (filename)
31021308Sache     char *filename;
31121308Sache{
31226497Sache  char *dirname, *expansion, *username;
31326497Sache  int user_len;
31426497Sache  struct passwd *user_entry;
31521308Sache
31626497Sache  if (filename == 0)
31721308Sache    return ((char *)NULL);
31821308Sache
31926497Sache  if (*filename != '~')
32026497Sache    return (savestring (filename));
32121308Sache
32226497Sache  /* A leading `~/' or a bare `~' is *always* translated to the value of
32326497Sache     $HOME or the home directory of the current user, regardless of any
32426497Sache     preexpansion hook. */
32526497Sache  if (filename[1] == '\0' || filename[1] == '/')
32621308Sache    {
32726497Sache      /* Prefix $HOME to the rest of the string. */
32826497Sache      expansion = get_string_value ("HOME");
32921308Sache
33021308Sache      /* If there is no HOME variable, look up the directory in
33121308Sache	 the password database. */
33226497Sache      if (expansion == 0)
33326497Sache	expansion = get_home_dir ();
33421308Sache
33526497Sache      return (glue_prefix_and_suffix (expansion, filename, 1));
33621308Sache    }
33721308Sache
33826497Sache  username = isolate_tilde_prefix (filename, &user_len);
33921308Sache
34026497Sache  if (tilde_expansion_preexpansion_hook)
34126497Sache    {
34226497Sache      expansion = (*tilde_expansion_preexpansion_hook) (username);
34326497Sache      if (expansion)
34421308Sache	{
34526497Sache	  dirname = glue_prefix_and_suffix (expansion, filename, user_len);
34626497Sache	  free (username);
34726497Sache	  free (expansion);
34826497Sache	  return (dirname);
34926497Sache	}
35026497Sache    }
35126497Sache
35226497Sache  /* No preexpansion hook, or the preexpansion hook failed.  Look in the
35326497Sache     password database. */
35426497Sache  dirname = (char *)NULL;
35526497Sache  user_entry = getpwnam (username);
35626497Sache  if (user_entry == 0)
35726497Sache    {
35826497Sache      /* If the calling program has a special syntax for expanding tildes,
35926497Sache	 and we couldn't find a standard expansion, then let them try. */
36026497Sache      if (tilde_expansion_failure_hook)
36126497Sache	{
36226497Sache	  expansion = (*tilde_expansion_failure_hook) (username);
36326497Sache	  if (expansion)
36421308Sache	    {
36526497Sache	      dirname = glue_prefix_and_suffix (expansion, filename, user_len);
36626497Sache	      free (expansion);
36721308Sache	    }
36821308Sache	}
36921308Sache      free (username);
37026497Sache      /* If we don't have a failure hook, or if the failure hook did not
37126497Sache	 expand the tilde, return a copy of what we were passed. */
37226497Sache      if (dirname == 0)
37326497Sache	dirname = savestring (filename);
37421308Sache    }
37526497Sache  else
37626497Sache    {
37726497Sache      free (username);
37826497Sache      dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
37926497Sache    }
38021308Sache
38126497Sache  endpwent ();
38221308Sache  return (dirname);
38321308Sache}
38421308Sache
38521308Sache
38621308Sache#if defined (TEST)
38721308Sache#undef NULL
38821308Sache#include <stdio.h>
38921308Sache
39021308Sachemain (argc, argv)
39121308Sache     int argc;
39221308Sache     char **argv;
39321308Sache{
39421308Sache  char *result, line[512];
39521308Sache  int done = 0;
39621308Sache
39721308Sache  while (!done)
39821308Sache    {
39921308Sache      printf ("~expand: ");
40021308Sache      fflush (stdout);
40121308Sache
40221308Sache      if (!gets (line))
40321308Sache	strcpy (line, "done");
40421308Sache
40521308Sache      if ((strcmp (line, "done") == 0) ||
40621308Sache	  (strcmp (line, "quit") == 0) ||
40721308Sache	  (strcmp (line, "exit") == 0))
40821308Sache	{
40921308Sache	  done = 1;
41021308Sache	  break;
41121308Sache	}
41221308Sache
41321308Sache      result = tilde_expand (line);
41421308Sache      printf ("  --> %s\n", result);
41521308Sache      free (result);
41621308Sache    }
41721308Sache  exit (0);
41821308Sache}
41921308Sache
42021308Sachestatic void memory_error_and_abort ();
42121308Sache
42221308Sachestatic char *
42321308Sachexmalloc (bytes)
42421308Sache     int bytes;
42521308Sache{
42621308Sache  char *temp = (char *)malloc (bytes);
42721308Sache
42821308Sache  if (!temp)
42921308Sache    memory_error_and_abort ();
43021308Sache  return (temp);
43121308Sache}
43221308Sache
43321308Sachestatic char *
43421308Sachexrealloc (pointer, bytes)
43521308Sache     char *pointer;
43621308Sache     int bytes;
43721308Sache{
43821308Sache  char *temp;
43921308Sache
44021308Sache  if (!pointer)
44121308Sache    temp = (char *)malloc (bytes);
44221308Sache  else
44321308Sache    temp = (char *)realloc (pointer, bytes);
44421308Sache
44521308Sache  if (!temp)
44621308Sache    memory_error_and_abort ();
44721308Sache
44821308Sache  return (temp);
44921308Sache}
45021308Sache
45121308Sachestatic void
45221308Sachememory_error_and_abort ()
45321308Sache{
45421308Sache  fprintf (stderr, "readline: out of virtual memory\n");
45521308Sache  abort ();
45621308Sache}
45721308Sache
45821308Sache/*
45921308Sache * Local variables:
46021308Sache * compile-command: "gcc -g -DTEST -o tilde tilde.c"
46121308Sache * end:
46221308Sache */
46321308Sache#endif /* TEST */
464