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