cmd-string.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <errno.h>
  19. #include <pwd.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include <unistd.h>
  24. #include "tmux.h"
  25. /*
  26. * Parse a command from a string.
  27. */
  28. int cmd_string_getc(const char *, size_t *);
  29. void cmd_string_ungetc(size_t *);
  30. void cmd_string_copy(char **, char *, size_t *);
  31. char *cmd_string_string(const char *, size_t *, char, int);
  32. char *cmd_string_variable(const char *, size_t *);
  33. char *cmd_string_expand_tilde(const char *, size_t *);
  34. int
  35. cmd_string_getc(const char *s, size_t *p)
  36. {
  37. const u_char *ucs = s;
  38. if (ucs[*p] == '\0')
  39. return (EOF);
  40. return (ucs[(*p)++]);
  41. }
  42. void
  43. cmd_string_ungetc(size_t *p)
  44. {
  45. (*p)--;
  46. }
  47. /*
  48. * Parse command string. Returns -1 on error. If returning -1, cause is error
  49. * string, or NULL for empty command.
  50. */
  51. int
  52. cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file,
  53. u_int line, char **cause)
  54. {
  55. size_t p;
  56. int ch, i, argc, rval;
  57. char **argv, *buf, *t;
  58. const char *whitespace, *equals;
  59. size_t len;
  60. argv = NULL;
  61. argc = 0;
  62. buf = NULL;
  63. len = 0;
  64. *cause = NULL;
  65. *cmdlist = NULL;
  66. rval = -1;
  67. p = 0;
  68. for (;;) {
  69. ch = cmd_string_getc(s, &p);
  70. switch (ch) {
  71. case '\'':
  72. if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
  73. goto error;
  74. cmd_string_copy(&buf, t, &len);
  75. break;
  76. case '"':
  77. if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
  78. goto error;
  79. cmd_string_copy(&buf, t, &len);
  80. break;
  81. case '$':
  82. if ((t = cmd_string_variable(s, &p)) == NULL)
  83. goto error;
  84. cmd_string_copy(&buf, t, &len);
  85. break;
  86. case '#':
  87. /* Comment: discard rest of line. */
  88. while ((ch = cmd_string_getc(s, &p)) != EOF)
  89. ;
  90. /* FALLTHROUGH */
  91. case EOF:
  92. case ' ':
  93. case '\t':
  94. if (buf != NULL) {
  95. buf = xrealloc(buf, len + 1);
  96. buf[len] = '\0';
  97. argv = xreallocarray(argv, argc + 1,
  98. sizeof *argv);
  99. argv[argc++] = buf;
  100. buf = NULL;
  101. len = 0;
  102. }
  103. if (ch != EOF)
  104. break;
  105. while (argc != 0) {
  106. equals = strchr(argv[0], '=');
  107. whitespace = argv[0] + strcspn(argv[0], " \t");
  108. if (equals == NULL || equals > whitespace)
  109. break;
  110. environ_put(global_environ, argv[0]);
  111. argc--;
  112. memmove(argv, argv + 1, argc * (sizeof *argv));
  113. }
  114. if (argc == 0)
  115. goto out;
  116. *cmdlist = cmd_list_parse(argc, argv, file, line, cause);
  117. if (*cmdlist == NULL)
  118. goto out;
  119. rval = 0;
  120. goto out;
  121. case '~':
  122. if (buf == NULL) {
  123. t = cmd_string_expand_tilde(s, &p);
  124. if (t == NULL)
  125. goto error;
  126. cmd_string_copy(&buf, t, &len);
  127. break;
  128. }
  129. /* FALLTHROUGH */
  130. default:
  131. if (len >= SIZE_MAX - 2)
  132. goto error;
  133. buf = xrealloc(buf, len + 1);
  134. buf[len++] = ch;
  135. break;
  136. }
  137. }
  138. error:
  139. xasprintf(cause, "invalid or unknown command: %s", s);
  140. out:
  141. free(buf);
  142. if (argv != NULL) {
  143. for (i = 0; i < argc; i++)
  144. free(argv[i]);
  145. free(argv);
  146. }
  147. return (rval);
  148. }
  149. void
  150. cmd_string_copy(char **dst, char *src, size_t *len)
  151. {
  152. size_t srclen;
  153. srclen = strlen(src);
  154. *dst = xrealloc(*dst, *len + srclen + 1);
  155. strlcpy(*dst + *len, src, srclen + 1);
  156. *len += srclen;
  157. free(src);
  158. }
  159. char *
  160. cmd_string_string(const char *s, size_t *p, char endch, int esc)
  161. {
  162. int ch;
  163. char *buf, *t;
  164. size_t len;
  165. buf = NULL;
  166. len = 0;
  167. while ((ch = cmd_string_getc(s, p)) != endch) {
  168. switch (ch) {
  169. case EOF:
  170. goto error;
  171. case '\\':
  172. if (!esc)
  173. break;
  174. switch (ch = cmd_string_getc(s, p)) {
  175. case EOF:
  176. goto error;
  177. case 'e':
  178. ch = '\033';
  179. break;
  180. case 'r':
  181. ch = '\r';
  182. break;
  183. case 'n':
  184. ch = '\n';
  185. break;
  186. case 't':
  187. ch = '\t';
  188. break;
  189. }
  190. break;
  191. case '$':
  192. if (!esc)
  193. break;
  194. if ((t = cmd_string_variable(s, p)) == NULL)
  195. goto error;
  196. cmd_string_copy(&buf, t, &len);
  197. continue;
  198. }
  199. if (len >= SIZE_MAX - 2)
  200. goto error;
  201. buf = xrealloc(buf, len + 1);
  202. buf[len++] = ch;
  203. }
  204. buf = xrealloc(buf, len + 1);
  205. buf[len] = '\0';
  206. return (buf);
  207. error:
  208. free(buf);
  209. return (NULL);
  210. }
  211. char *
  212. cmd_string_variable(const char *s, size_t *p)
  213. {
  214. int ch, fch;
  215. char *buf, *t;
  216. size_t len;
  217. struct environ_entry *envent;
  218. #define cmd_string_first(ch) ((ch) == '_' || \
  219. ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
  220. #define cmd_string_other(ch) ((ch) == '_' || \
  221. ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
  222. ((ch) >= '0' && (ch) <= '9'))
  223. buf = NULL;
  224. len = 0;
  225. fch = EOF;
  226. switch (ch = cmd_string_getc(s, p)) {
  227. case EOF:
  228. goto error;
  229. case '{':
  230. fch = '{';
  231. ch = cmd_string_getc(s, p);
  232. if (!cmd_string_first(ch))
  233. goto error;
  234. /* FALLTHROUGH */
  235. default:
  236. if (!cmd_string_first(ch)) {
  237. xasprintf(&t, "$%c", ch);
  238. return (t);
  239. }
  240. buf = xrealloc(buf, len + 1);
  241. buf[len++] = ch;
  242. for (;;) {
  243. ch = cmd_string_getc(s, p);
  244. if (ch == EOF || !cmd_string_other(ch))
  245. break;
  246. else {
  247. if (len >= SIZE_MAX - 3)
  248. goto error;
  249. buf = xrealloc(buf, len + 1);
  250. buf[len++] = ch;
  251. }
  252. }
  253. }
  254. if (fch == '{' && ch != '}')
  255. goto error;
  256. if (ch != EOF && fch != '{')
  257. cmd_string_ungetc(p); /* ch */
  258. buf = xrealloc(buf, len + 1);
  259. buf[len] = '\0';
  260. envent = environ_find(global_environ, buf);
  261. free(buf);
  262. if (envent == NULL)
  263. return (xstrdup(""));
  264. return (xstrdup(envent->value));
  265. error:
  266. free(buf);
  267. return (NULL);
  268. }
  269. char *
  270. cmd_string_expand_tilde(const char *s, size_t *p)
  271. {
  272. struct passwd *pw;
  273. struct environ_entry *envent;
  274. char *home, *path, *user, *cp;
  275. int last;
  276. home = NULL;
  277. last = cmd_string_getc(s, p);
  278. if (last == EOF || last == '/' || last == ' '|| last == '\t') {
  279. envent = environ_find(global_environ, "HOME");
  280. if (envent != NULL && *envent->value != '\0')
  281. home = envent->value;
  282. else if ((pw = getpwuid(getuid())) != NULL)
  283. home = pw->pw_dir;
  284. } else {
  285. cmd_string_ungetc(p);
  286. cp = user = xmalloc(strlen(s));
  287. for (;;) {
  288. last = cmd_string_getc(s, p);
  289. if (last == EOF || last == '/' || last == ' '|| last == '\t')
  290. break;
  291. *cp++ = last;
  292. }
  293. *cp = '\0';
  294. if ((pw = getpwnam(user)) != NULL)
  295. home = pw->pw_dir;
  296. free(user);
  297. }
  298. if (home == NULL)
  299. return (NULL);
  300. if (last != EOF)
  301. xasprintf(&path, "%s%c", home, last);
  302. else
  303. xasprintf(&path, "%s", home);
  304. return (path);
  305. }