Annotation of embedaddon/readline/tilde.c, revision 1.1

1.1     ! misho       1: /* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */
        !             2: 
        !             3: /* Copyright (C) 1988-2009 Free Software Foundation, Inc.
        !             4: 
        !             5:    This file is part of the GNU Readline Library (Readline), a library
        !             6:    for reading lines of text with interactive input and history editing.
        !             7: 
        !             8:    Readline is free software: you can redistribute it and/or modify
        !             9:    it under the terms of the GNU General Public License as published by
        !            10:    the Free Software Foundation, either version 3 of the License, or
        !            11:    (at your option) any later version.
        !            12: 
        !            13:    Readline is distributed in the hope that it will be useful,
        !            14:    but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            15:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            16:    GNU General Public License for more details.
        !            17: 
        !            18:    You should have received a copy of the GNU General Public License
        !            19:    along with Readline.  If not, see <http://www.gnu.org/licenses/>.
        !            20: */
        !            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: #if defined (HAVE_PWD_H)
        !            47: #include <pwd.h>
        !            48: #endif
        !            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_GETPW_DECLS)
        !            59: #  if defined (HAVE_GETPWUID)
        !            60: extern struct passwd *getpwuid PARAMS((uid_t));
        !            61: #  endif
        !            62: #  if defined (HAVE_GETPWNAM)
        !            63: extern struct passwd *getpwnam PARAMS((const char *));
        !            64: #  endif
        !            65: #endif /* !HAVE_GETPW_DECLS */
        !            66: 
        !            67: #if !defined (savestring)
        !            68: #define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
        !            69: #endif /* !savestring */
        !            70: 
        !            71: #if !defined (NULL)
        !            72: #  if defined (__STDC__)
        !            73: #    define NULL ((void *) 0)
        !            74: #  else
        !            75: #    define NULL 0x0
        !            76: #  endif /* !__STDC__ */
        !            77: #endif /* !NULL */
        !            78: 
        !            79: /* If being compiled as part of bash, these will be satisfied from
        !            80:    variables.o.  If being compiled as part of readline, they will
        !            81:    be satisfied from shell.o. */
        !            82: extern char *sh_get_home_dir PARAMS((void));
        !            83: extern char *sh_get_env_value PARAMS((const char *));
        !            84: 
        !            85: /* The default value of tilde_additional_prefixes.  This is set to
        !            86:    whitespace preceding a tilde so that simple programs which do not
        !            87:    perform any word separation get desired behaviour. */
        !            88: static const char *default_prefixes[] =
        !            89:   { " ~", "\t~", (const char *)NULL };
        !            90: 
        !            91: /* The default value of tilde_additional_suffixes.  This is set to
        !            92:    whitespace or newline so that simple programs which do not
        !            93:    perform any word separation get desired behaviour. */
        !            94: static const char *default_suffixes[] =
        !            95:   { " ", "\n", (const char *)NULL };
        !            96: 
        !            97: /* If non-null, this contains the address of a function that the application
        !            98:    wants called before trying the standard tilde expansions.  The function
        !            99:    is called with the text sans tilde, and returns a malloc()'ed string
        !           100:    which is the expansion, or a NULL pointer if the expansion fails. */
        !           101: tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;
        !           102: 
        !           103: /* If non-null, this contains the address of a function to call if the
        !           104:    standard meaning for expanding a tilde fails.  The function is called
        !           105:    with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
        !           106:    which is the expansion, or a NULL pointer if there is no expansion. */
        !           107: tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;
        !           108: 
        !           109: /* When non-null, this is a NULL terminated array of strings which
        !           110:    are duplicates for a tilde prefix.  Bash uses this to expand
        !           111:    `=~' and `:~'. */
        !           112: char **tilde_additional_prefixes = (char **)default_prefixes;
        !           113: 
        !           114: /* When non-null, this is a NULL terminated array of strings which match
        !           115:    the end of a username, instead of just "/".  Bash sets this to
        !           116:    `:' and `=~'. */
        !           117: char **tilde_additional_suffixes = (char **)default_suffixes;
        !           118: 
        !           119: static int tilde_find_prefix PARAMS((const char *, int *));
        !           120: static int tilde_find_suffix PARAMS((const char *));
        !           121: static char *isolate_tilde_prefix PARAMS((const char *, int *));
        !           122: static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));
        !           123: 
        !           124: /* Find the start of a tilde expansion in STRING, and return the index of
        !           125:    the tilde which starts the expansion.  Place the length of the text
        !           126:    which identified this tilde starter in LEN, excluding the tilde itself. */
        !           127: static int
        !           128: tilde_find_prefix (string, len)
        !           129:      const char *string;
        !           130:      int *len;
        !           131: {
        !           132:   register int i, j, string_len;
        !           133:   register char **prefixes;
        !           134: 
        !           135:   prefixes = tilde_additional_prefixes;
        !           136: 
        !           137:   string_len = strlen (string);
        !           138:   *len = 0;
        !           139: 
        !           140:   if (*string == '\0' || *string == '~')
        !           141:     return (0);
        !           142: 
        !           143:   if (prefixes)
        !           144:     {
        !           145:       for (i = 0; i < string_len; i++)
        !           146:        {
        !           147:          for (j = 0; prefixes[j]; j++)
        !           148:            {
        !           149:              if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
        !           150:                {
        !           151:                  *len = strlen (prefixes[j]) - 1;
        !           152:                  return (i + *len);
        !           153:                }
        !           154:            }
        !           155:        }
        !           156:     }
        !           157:   return (string_len);
        !           158: }
        !           159: 
        !           160: /* Find the end of a tilde expansion in STRING, and return the index of
        !           161:    the character which ends the tilde definition.  */
        !           162: static int
        !           163: tilde_find_suffix (string)
        !           164:      const char *string;
        !           165: {
        !           166:   register int i, j, string_len;
        !           167:   register char **suffixes;
        !           168: 
        !           169:   suffixes = tilde_additional_suffixes;
        !           170:   string_len = strlen (string);
        !           171: 
        !           172:   for (i = 0; i < string_len; i++)
        !           173:     {
        !           174: #if defined (__MSDOS__)
        !           175:       if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
        !           176: #else
        !           177:       if (string[i] == '/' /* || !string[i] */)
        !           178: #endif
        !           179:        break;
        !           180: 
        !           181:       for (j = 0; suffixes && suffixes[j]; j++)
        !           182:        {
        !           183:          if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
        !           184:            return (i);
        !           185:        }
        !           186:     }
        !           187:   return (i);
        !           188: }
        !           189: 
        !           190: /* Return a new string which is the result of tilde expanding STRING. */
        !           191: char *
        !           192: tilde_expand (string)
        !           193:      const char *string;
        !           194: {
        !           195:   char *result;
        !           196:   int result_size, result_index;
        !           197: 
        !           198:   result_index = result_size = 0;
        !           199:   if (result = strchr (string, '~'))
        !           200:     result = (char *)xmalloc (result_size = (strlen (string) + 16));
        !           201:   else
        !           202:     result = (char *)xmalloc (result_size = (strlen (string) + 1));
        !           203: 
        !           204:   /* Scan through STRING expanding tildes as we come to them. */
        !           205:   while (1)
        !           206:     {
        !           207:       register int start, end;
        !           208:       char *tilde_word, *expansion;
        !           209:       int len;
        !           210: 
        !           211:       /* Make START point to the tilde which starts the expansion. */
        !           212:       start = tilde_find_prefix (string, &len);
        !           213: 
        !           214:       /* Copy the skipped text into the result. */
        !           215:       if ((result_index + start + 1) > result_size)
        !           216:        result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
        !           217: 
        !           218:       strncpy (result + result_index, string, start);
        !           219:       result_index += start;
        !           220: 
        !           221:       /* Advance STRING to the starting tilde. */
        !           222:       string += start;
        !           223: 
        !           224:       /* Make END be the index of one after the last character of the
        !           225:         username. */
        !           226:       end = tilde_find_suffix (string);
        !           227: 
        !           228:       /* If both START and END are zero, we are all done. */
        !           229:       if (!start && !end)
        !           230:        break;
        !           231: 
        !           232:       /* Expand the entire tilde word, and copy it into RESULT. */
        !           233:       tilde_word = (char *)xmalloc (1 + end);
        !           234:       strncpy (tilde_word, string, end);
        !           235:       tilde_word[end] = '\0';
        !           236:       string += end;
        !           237: 
        !           238:       expansion = tilde_expand_word (tilde_word);
        !           239:       xfree (tilde_word);
        !           240: 
        !           241:       len = strlen (expansion);
        !           242: #ifdef __CYGWIN__
        !           243:       /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
        !           244:         $HOME for `user' is /.  On cygwin, // denotes a network drive. */
        !           245:       if (len > 1 || *expansion != '/' || *string != '/')
        !           246: #endif
        !           247:        {
        !           248:          if ((result_index + len + 1) > result_size)
        !           249:            result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
        !           250: 
        !           251:          strcpy (result + result_index, expansion);
        !           252:          result_index += len;
        !           253:        }
        !           254:       xfree (expansion);
        !           255:     }
        !           256: 
        !           257:   result[result_index] = '\0';
        !           258: 
        !           259:   return (result);
        !           260: }
        !           261: 
        !           262: /* Take FNAME and return the tilde prefix we want expanded.  If LENP is
        !           263:    non-null, the index of the end of the prefix into FNAME is returned in
        !           264:    the location it points to. */
        !           265: static char *
        !           266: isolate_tilde_prefix (fname, lenp)
        !           267:      const char *fname;
        !           268:      int *lenp;
        !           269: {
        !           270:   char *ret;
        !           271:   int i;
        !           272: 
        !           273:   ret = (char *)xmalloc (strlen (fname));
        !           274: #if defined (__MSDOS__)
        !           275:   for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
        !           276: #else
        !           277:   for (i = 1; fname[i] && fname[i] != '/'; i++)
        !           278: #endif
        !           279:     ret[i - 1] = fname[i];
        !           280:   ret[i - 1] = '\0';
        !           281:   if (lenp)
        !           282:     *lenp = i;
        !           283:   return ret;
        !           284: }
        !           285: 
        !           286: #if 0
        !           287: /* Public function to scan a string (FNAME) beginning with a tilde and find
        !           288:    the portion of the string that should be passed to the tilde expansion
        !           289:    function.  Right now, it just calls tilde_find_suffix and allocates new
        !           290:    memory, but it can be expanded to do different things later. */
        !           291: char *
        !           292: tilde_find_word (fname, flags, lenp)
        !           293:      const char *fname;
        !           294:      int flags, *lenp;
        !           295: {
        !           296:   int x;
        !           297:   char *r;
        !           298: 
        !           299:   x = tilde_find_suffix (fname);
        !           300:   if (x == 0)
        !           301:     {
        !           302:       r = savestring (fname);
        !           303:       if (lenp)
        !           304:        *lenp = 0;
        !           305:     }
        !           306:   else
        !           307:     {
        !           308:       r = (char *)xmalloc (1 + x);
        !           309:       strncpy (r, fname, x);
        !           310:       r[x] = '\0';
        !           311:       if (lenp)
        !           312:        *lenp = x;
        !           313:     }
        !           314: 
        !           315:   return r;
        !           316: }
        !           317: #endif
        !           318: 
        !           319: /* Return a string that is PREFIX concatenated with SUFFIX starting at
        !           320:    SUFFIND. */
        !           321: static char *
        !           322: glue_prefix_and_suffix (prefix, suffix, suffind)
        !           323:      char *prefix;
        !           324:      const char *suffix;
        !           325:      int suffind;
        !           326: {
        !           327:   char *ret;
        !           328:   int plen, slen;
        !           329: 
        !           330:   plen = (prefix && *prefix) ? strlen (prefix) : 0;
        !           331:   slen = strlen (suffix + suffind);
        !           332:   ret = (char *)xmalloc (plen + slen + 1);
        !           333:   if (plen)
        !           334:     strcpy (ret, prefix);
        !           335:   strcpy (ret + plen, suffix + suffind);
        !           336:   return ret;
        !           337: }
        !           338: 
        !           339: /* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
        !           340:    tilde.  If there is no expansion, call tilde_expansion_failure_hook.
        !           341:    This always returns a newly-allocated string, never static storage. */
        !           342: char *
        !           343: tilde_expand_word (filename)
        !           344:      const char *filename;
        !           345: {
        !           346:   char *dirname, *expansion, *username;
        !           347:   int user_len;
        !           348:   struct passwd *user_entry;
        !           349: 
        !           350:   if (filename == 0)
        !           351:     return ((char *)NULL);
        !           352: 
        !           353:   if (*filename != '~')
        !           354:     return (savestring (filename));
        !           355: 
        !           356:   /* A leading `~/' or a bare `~' is *always* translated to the value of
        !           357:      $HOME or the home directory of the current user, regardless of any
        !           358:      preexpansion hook. */
        !           359:   if (filename[1] == '\0' || filename[1] == '/')
        !           360:     {
        !           361:       /* Prefix $HOME to the rest of the string. */
        !           362:       expansion = sh_get_env_value ("HOME");
        !           363: 
        !           364:       /* If there is no HOME variable, look up the directory in
        !           365:         the password database. */
        !           366:       if (expansion == 0)
        !           367:        expansion = sh_get_home_dir ();
        !           368: 
        !           369:       return (glue_prefix_and_suffix (expansion, filename, 1));
        !           370:     }
        !           371: 
        !           372:   username = isolate_tilde_prefix (filename, &user_len);
        !           373: 
        !           374:   if (tilde_expansion_preexpansion_hook)
        !           375:     {
        !           376:       expansion = (*tilde_expansion_preexpansion_hook) (username);
        !           377:       if (expansion)
        !           378:        {
        !           379:          dirname = glue_prefix_and_suffix (expansion, filename, user_len);
        !           380:          xfree (username);
        !           381:          xfree (expansion);
        !           382:          return (dirname);
        !           383:        }
        !           384:     }
        !           385: 
        !           386:   /* No preexpansion hook, or the preexpansion hook failed.  Look in the
        !           387:      password database. */
        !           388:   dirname = (char *)NULL;
        !           389: #if defined (HAVE_GETPWNAM)
        !           390:   user_entry = getpwnam (username);
        !           391: #else
        !           392:   user_entry = 0;
        !           393: #endif
        !           394:   if (user_entry == 0)
        !           395:     {
        !           396:       /* If the calling program has a special syntax for expanding tildes,
        !           397:         and we couldn't find a standard expansion, then let them try. */
        !           398:       if (tilde_expansion_failure_hook)
        !           399:        {
        !           400:          expansion = (*tilde_expansion_failure_hook) (username);
        !           401:          if (expansion)
        !           402:            {
        !           403:              dirname = glue_prefix_and_suffix (expansion, filename, user_len);
        !           404:              xfree (expansion);
        !           405:            }
        !           406:        }
        !           407:       /* If we don't have a failure hook, or if the failure hook did not
        !           408:         expand the tilde, return a copy of what we were passed. */
        !           409:       if (dirname == 0)
        !           410:        dirname = savestring (filename);
        !           411:     }
        !           412: #if defined (HAVE_GETPWENT)
        !           413:   else
        !           414:     dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
        !           415: #endif
        !           416: 
        !           417:   xfree (username);
        !           418: #if defined (HAVE_GETPWENT)
        !           419:   endpwent ();
        !           420: #endif
        !           421:   return (dirname);
        !           422: }
        !           423: 
        !           424: 
        !           425: #if defined (TEST)
        !           426: #undef NULL
        !           427: #include <stdio.h>
        !           428: 
        !           429: main (argc, argv)
        !           430:      int argc;
        !           431:      char **argv;
        !           432: {
        !           433:   char *result, line[512];
        !           434:   int done = 0;
        !           435: 
        !           436:   while (!done)
        !           437:     {
        !           438:       printf ("~expand: ");
        !           439:       fflush (stdout);
        !           440: 
        !           441:       if (!gets (line))
        !           442:        strcpy (line, "done");
        !           443: 
        !           444:       if ((strcmp (line, "done") == 0) ||
        !           445:          (strcmp (line, "quit") == 0) ||
        !           446:          (strcmp (line, "exit") == 0))
        !           447:        {
        !           448:          done = 1;
        !           449:          break;
        !           450:        }
        !           451: 
        !           452:       result = tilde_expand (line);
        !           453:       printf ("  --> %s\n", result);
        !           454:       free (result);
        !           455:     }
        !           456:   exit (0);
        !           457: }
        !           458: 
        !           459: static void memory_error_and_abort ();
        !           460: 
        !           461: static void *
        !           462: xmalloc (bytes)
        !           463:      size_t bytes;
        !           464: {
        !           465:   void *temp = (char *)malloc (bytes);
        !           466: 
        !           467:   if (!temp)
        !           468:     memory_error_and_abort ();
        !           469:   return (temp);
        !           470: }
        !           471: 
        !           472: static void *
        !           473: xrealloc (pointer, bytes)
        !           474:      void *pointer;
        !           475:      int bytes;
        !           476: {
        !           477:   void *temp;
        !           478: 
        !           479:   if (!pointer)
        !           480:     temp = malloc (bytes);
        !           481:   else
        !           482:     temp = realloc (pointer, bytes);
        !           483: 
        !           484:   if (!temp)
        !           485:     memory_error_and_abort ();
        !           486: 
        !           487:   return (temp);
        !           488: }
        !           489: 
        !           490: static void
        !           491: memory_error_and_abort ()
        !           492: {
        !           493:   fprintf (stderr, "readline: out of virtual memory\n");
        !           494:   abort ();
        !           495: }
        !           496: 
        !           497: /*
        !           498:  * Local variables:
        !           499:  * compile-command: "gcc -g -DTEST -o tilde tilde.c"
        !           500:  * end:
        !           501:  */
        !           502: #endif /* TEST */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>