Back to index

tetex-bin  3.0
tilde.c
Go to the documentation of this file.
00001 /* tilde.c -- tilde expansion code (~/foo := $HOME/foo).
00002    $Id: tilde.c,v 1.3 2004/04/11 17:56:46 karl Exp $
00003 
00004    Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1998, 1999,
00005    2002, 2004 Free Software Foundation, Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 
00021    Written by Brian Fox (bfox@ai.mit.edu). */
00022 
00023 /* Include config.h before doing alloca.  */
00024 #include "info.h"
00025 #include "tilde.h"
00026 
00027 #if defined (TEST) || defined (STATIC_MALLOC)
00028 static void *xmalloc (), *xrealloc ();
00029 #endif /* TEST || STATIC_MALLOC */
00030 
00031 /* The default value of tilde_additional_prefixes.  This is set to
00032    whitespace preceding a tilde so that simple programs which do not
00033    perform any word separation get desired behaviour. */
00034 static char *default_prefixes[] =
00035   { " ~", "\t~", (char *)NULL };
00036 
00037 /* The default value of tilde_additional_suffixes.  This is set to
00038    whitespace or newline so that simple programs which do not
00039    perform any word separation get desired behaviour. */
00040 static char *default_suffixes[] =
00041   { " ", "\n", (char *)NULL };
00042 
00043 /* If non-null, this contains the address of a function to call if the
00044    standard meaning for expanding a tilde fails.  The function is called
00045    with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
00046    which is the expansion, or a NULL pointer if there is no expansion. */
00047 CFunction *tilde_expansion_failure_hook = (CFunction *)NULL;
00048 
00049 /* When non-null, this is a NULL terminated array of strings which
00050    are duplicates for a tilde prefix.  Bash uses this to expand
00051    `=~' and `:~'. */
00052 char **tilde_additional_prefixes = default_prefixes;
00053 
00054 /* When non-null, this is a NULL terminated array of strings which match
00055    the end of a username, instead of just "/".  Bash sets this to
00056    `:' and `=~'. */
00057 char **tilde_additional_suffixes = default_suffixes;
00058 
00059 /* Find the start of a tilde expansion in STRING, and return the index of
00060    the tilde which starts the expansion.  Place the length of the text
00061    which identified this tilde starter in LEN, excluding the tilde itself. */
00062 static int
00063 tilde_find_prefix (char *string, int *len)
00064 {
00065   register int i, j, string_len;
00066   register char **prefixes = tilde_additional_prefixes;
00067 
00068   string_len = strlen (string);
00069   *len = 0;
00070 
00071   if (!*string || *string == '~')
00072     return (0);
00073 
00074   if (prefixes)
00075     {
00076       for (i = 0; i < string_len; i++)
00077         {
00078           for (j = 0; prefixes[j]; j++)
00079             {
00080               if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
00081                 {
00082                   *len = strlen (prefixes[j]) - 1;
00083                   return (i + *len);
00084                 }
00085             }
00086         }
00087     }
00088   return (string_len);
00089 }
00090 
00091 /* Find the end of a tilde expansion in STRING, and return the index of
00092    the character which ends the tilde definition.  */
00093 static int
00094 tilde_find_suffix (char *string)
00095 {
00096   register int i, j, string_len;
00097   register char **suffixes = tilde_additional_suffixes;
00098 
00099   string_len = strlen (string);
00100 
00101   for (i = 0; i < string_len; i++)
00102     {
00103       if (IS_SLASH (string[i]) || !string[i])
00104         break;
00105 
00106       for (j = 0; suffixes && suffixes[j]; j++)
00107         {
00108           if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
00109             return (i);
00110         }
00111     }
00112   return (i);
00113 }
00114 
00115 /* Return a new string which is the result of tilde expanding STRING. */
00116 char *
00117 tilde_expand (char *string)
00118 {
00119   char *result;
00120   int result_size, result_index;
00121 
00122   result_size = result_index = 0;
00123   result = (char *)NULL;
00124 
00125   /* Scan through STRING expanding tildes as we come to them. */
00126   while (1)
00127     {
00128       register int start, end;
00129       char *tilde_word, *expansion;
00130       int len;
00131 
00132       /* Make START point to the tilde which starts the expansion. */
00133       start = tilde_find_prefix (string, &len);
00134 
00135       /* Copy the skipped text into the result. */
00136       if ((result_index + start + 1) > result_size)
00137         result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
00138 
00139       strncpy (result + result_index, string, start);
00140       result_index += start;
00141 
00142       /* Advance STRING to the starting tilde. */
00143       string += start;
00144 
00145       /* Make END be the index of one after the last character of the
00146          username. */
00147       end = tilde_find_suffix (string);
00148 
00149       /* If both START and END are zero, we are all done. */
00150       if (!start && !end)
00151         break;
00152 
00153       /* Expand the entire tilde word, and copy it into RESULT. */
00154       tilde_word = (char *)xmalloc (1 + end);
00155       strncpy (tilde_word, string, end);
00156       tilde_word[end] = '\0';
00157       string += end;
00158 
00159       expansion = tilde_expand_word (tilde_word);
00160       free (tilde_word);
00161 
00162       len = strlen (expansion);
00163       if ((result_index + len + 1) > result_size)
00164         result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
00165 
00166       strcpy (result + result_index, expansion);
00167       result_index += len;
00168       free (expansion);
00169     }
00170 
00171   result[result_index] = '\0';
00172 
00173   return (result);
00174 }
00175 
00176 /* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
00177    tilde.  If there is no expansion, call tilde_expansion_failure_hook. */
00178 char *
00179 tilde_expand_word (char *filename)
00180 {
00181   char *dirname = filename ? xstrdup (filename) : NULL;
00182 
00183   if (dirname && *dirname == '~')
00184     {
00185       char *temp_name;
00186       if (!dirname[1] || IS_SLASH (dirname[1]))
00187         {
00188           /* Prepend $HOME to the rest of the string. */
00189           char *temp_home = getenv ("HOME");
00190 
00191           /* If there is no HOME variable, look up the directory in
00192              the password database. */
00193           if (!temp_home)
00194             {
00195               struct passwd *entry;
00196 
00197               entry = (struct passwd *) getpwuid (getuid ());
00198               if (entry)
00199                 temp_home = entry->pw_dir;
00200             }
00201 
00202           temp_name = xmalloc (1 + strlen (&dirname[1])
00203                                + (temp_home ? strlen (temp_home) : 0));
00204           if (temp_home)
00205             strcpy (temp_name, temp_home);
00206           else
00207             temp_name[0] = 0;
00208           strcat (temp_name, &dirname[1]);
00209           free (dirname);
00210           dirname = xstrdup (temp_name);
00211           free (temp_name);
00212         }
00213       else
00214         {
00215           struct passwd *user_entry;
00216           char *username = xmalloc (257);
00217           int i, c;
00218 
00219           for (i = 1; (c = dirname[i]); i++)
00220             {
00221               if (IS_SLASH (c))
00222                 break;
00223               else
00224                 username[i - 1] = c;
00225             }
00226           username[i - 1] = 0;
00227 
00228           if (!(user_entry = (struct passwd *) getpwnam (username)))
00229             {
00230               /* If the calling program has a special syntax for
00231                  expanding tildes, and we couldn't find a standard
00232                  expansion, then let them try. */
00233               if (tilde_expansion_failure_hook)
00234                 {
00235                   char *expansion = (*tilde_expansion_failure_hook) (username);
00236 
00237                   if (expansion)
00238                     {
00239                       temp_name = xmalloc (1 + strlen (expansion)
00240                                            + strlen (&dirname[i])); 
00241                       strcpy (temp_name, expansion);
00242                       strcat (temp_name, &dirname[i]);
00243                       free (expansion);
00244                       goto return_name;
00245                     }
00246                 }
00247               /* We shouldn't report errors. */
00248             }
00249           else
00250             {
00251               temp_name = xmalloc (1 + strlen (user_entry->pw_dir)
00252                                    + strlen (&dirname[i])); 
00253               strcpy (temp_name, user_entry->pw_dir);
00254               strcat (temp_name, &dirname[i]);
00255 
00256             return_name:
00257               free (dirname);
00258               dirname = xstrdup (temp_name);
00259               free (temp_name);
00260             }
00261 
00262           endpwent ();
00263           free (username);
00264         }
00265     }
00266   return dirname;
00267 }
00268 
00269 
00270 #if defined (TEST)
00271 #undef NULL
00272 #include <stdio.h>
00273 
00274 main (argc, argv)
00275      int argc;
00276      char **argv;
00277 {
00278   char *result, line[512];
00279   int done = 0;
00280 
00281   while (!done)
00282     {
00283       printf ("~expand: ");
00284       fflush (stdout);
00285 
00286       if (!gets (line))
00287         strcpy (line, "done");
00288 
00289       if ((strcmp (line, "done") == 0) ||
00290           (strcmp (line, "quit") == 0) ||
00291           (strcmp (line, "exit") == 0))
00292         {
00293           done = 1;
00294           break;
00295         }
00296 
00297       result = tilde_expand (line);
00298       printf ("  --> %s\n", result);
00299       free (result);
00300     }
00301   xexit (0);
00302 }
00303 
00304 static void memory_error_and_abort ();
00305 
00306 static void *
00307 xmalloc (bytes)
00308      int bytes;
00309 {
00310   void *temp = (void *)malloc (bytes);
00311 
00312   if (!temp)
00313     memory_error_and_abort ();
00314   return (temp);
00315 }
00316 
00317 static void *
00318 xrealloc (pointer, bytes)
00319      void *pointer;
00320      int bytes;
00321 {
00322   void *temp;
00323 
00324   if (!pointer)
00325     temp = (char *)malloc (bytes);
00326   else
00327     temp = (char *)realloc (pointer, bytes);
00328 
00329   if (!temp)
00330     memory_error_and_abort ();
00331 
00332   return (temp);
00333 }
00334 
00335 static void
00336 memory_error_and_abort ()
00337 {
00338   fprintf (stderr, _("readline: Out of virtual memory!\n"));
00339   abort ();
00340 }
00341 #endif /* TEST */
00342