tilde.c 13 KB

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