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>