1 /* Id */ 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 34 int cmd_string_getc(const char *, size_t *); 35 void cmd_string_ungetc(size_t *); 36 void cmd_string_copy(char **, char *, size_t *); 37 char *cmd_string_string(const char *, size_t *, char, int); 38 char *cmd_string_variable(const char *, size_t *); 39 char *cmd_string_expand_tilde(const char *, size_t *); 40 41 int 42 cmd_string_getc(const char *s, size_t *p) 43 { 44 const char *ucs = s; 45 46 if (ucs[*p] == '\0') 47 return (EOF); 48 return (u_char)(ucs[(*p)++]); 49 } 50 51 void 52 cmd_string_ungetc(size_t *p) 53 { 54 (*p)--; 55 } 56 57 /* 58 * Parse command string. Returns -1 on error. If returning -1, cause is error 59 * string, or NULL for empty command. 60 */ 61 int 62 cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file, 63 u_int line, char **cause) 64 { 65 size_t p; 66 int ch, i, argc, rval; 67 char **argv, *buf, *t; 68 const char *whitespace, *equals; 69 size_t len; 70 71 argv = NULL; 72 argc = 0; 73 74 buf = NULL; 75 len = 0; 76 77 *cause = NULL; 78 79 *cmdlist = NULL; 80 rval = -1; 81 82 p = 0; 83 for (;;) { 84 ch = cmd_string_getc(s, &p); 85 switch (ch) { 86 case '\'': 87 if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL) 88 goto error; 89 cmd_string_copy(&buf, t, &len); 90 break; 91 case '"': 92 if ((t = cmd_string_string(s, &p, '"', 1)) == NULL) 93 goto error; 94 cmd_string_copy(&buf, t, &len); 95 break; 96 case '$': 97 if ((t = cmd_string_variable(s, &p)) == NULL) 98 goto error; 99 cmd_string_copy(&buf, t, &len); 100 break; 101 case '#': 102 /* Comment: discard rest of line. */ 103 while ((ch = cmd_string_getc(s, &p)) != EOF) 104 ; 105 /* FALLTHROUGH */ 106 case EOF: 107 case ' ': 108 case '\t': 109 if (buf != NULL) { 110 buf = xrealloc(buf, 1, len + 1); 111 buf[len] = '\0'; 112 113 argv = xrealloc(argv, argc + 1, sizeof *argv); 114 argv[argc++] = buf; 115 116 buf = NULL; 117 len = 0; 118 } 119 120 if (ch != EOF) 121 break; 122 123 while (argc != 0) { 124 equals = strchr(argv[0], '='); 125 whitespace = argv[0] + strcspn(argv[0], " \t"); 126 if (equals == NULL || equals > whitespace) 127 break; 128 environ_put(&global_environ, argv[0]); 129 argc--; 130 memmove(argv, argv + 1, argc * (sizeof *argv)); 131 } 132 if (argc == 0) 133 goto out; 134 135 *cmdlist = cmd_list_parse(argc, argv, file, line, cause); 136 if (*cmdlist == NULL) 137 goto out; 138 139 rval = 0; 140 goto out; 141 case '~': 142 if (buf == NULL) { 143 t = cmd_string_expand_tilde(s, &p); 144 if (t == NULL) 145 goto error; 146 cmd_string_copy(&buf, t, &len); 147 break; 148 } 149 /* FALLTHROUGH */ 150 default: 151 if (len >= SIZE_MAX - 2) 152 goto error; 153 154 buf = xrealloc(buf, 1, len + 1); 155 buf[len++] = ch; 156 break; 157 } 158 } 159 160 error: 161 xasprintf(cause, "invalid or unknown command: %s", s); 162 163 out: 164 free(buf); 165 166 if (argv != NULL) { 167 for (i = 0; i < argc; i++) 168 free(argv[i]); 169 free(argv); 170 } 171 172 return (rval); 173 } 174 175 void 176 cmd_string_copy(char **dst, char *src, size_t *len) 177 { 178 size_t srclen; 179 180 srclen = strlen(src); 181 182 *dst = xrealloc(*dst, 1, *len + srclen + 1); 183 strlcpy(*dst + *len, src, srclen + 1); 184 185 *len += srclen; 186 free(src); 187 } 188 189 char * 190 cmd_string_string(const char *s, size_t *p, char endch, int esc) 191 { 192 int ch; 193 char *buf, *t; 194 size_t len; 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 cmd_string_copy(&buf, t, &len); 229 continue; 230 } 231 232 if (len >= SIZE_MAX - 2) 233 goto error; 234 buf = xrealloc(buf, 1, len + 1); 235 buf[len++] = ch; 236 } 237 238 buf = xrealloc(buf, 1, len + 1); 239 buf[len] = '\0'; 240 return (buf); 241 242 error: 243 free(buf); 244 return (NULL); 245 } 246 247 char * 248 cmd_string_variable(const char *s, size_t *p) 249 { 250 int ch, fch; 251 char *buf, *t; 252 size_t len; 253 struct environ_entry *envent; 254 255 #define cmd_string_first(ch) ((ch) == '_' || \ 256 ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) 257 #define cmd_string_other(ch) ((ch) == '_' || \ 258 ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \ 259 ((ch) >= '0' && (ch) <= '9')) 260 261 buf = NULL; 262 len = 0; 263 264 fch = EOF; 265 switch (ch = cmd_string_getc(s, p)) { 266 case EOF: 267 goto error; 268 case '{': 269 fch = '{'; 270 271 ch = cmd_string_getc(s, p); 272 if (!cmd_string_first(ch)) 273 goto error; 274 /* FALLTHROUGH */ 275 default: 276 if (!cmd_string_first(ch)) { 277 xasprintf(&t, "$%c", ch); 278 return (t); 279 } 280 281 buf = xrealloc(buf, 1, len + 1); 282 buf[len++] = ch; 283 284 for (;;) { 285 ch = cmd_string_getc(s, p); 286 if (ch == EOF || !cmd_string_other(ch)) 287 break; 288 else { 289 if (len >= SIZE_MAX - 3) 290 goto error; 291 buf = xrealloc(buf, 1, len + 1); 292 buf[len++] = ch; 293 } 294 } 295 } 296 297 if (fch == '{' && ch != '}') 298 goto error; 299 if (ch != EOF && fch != '{') 300 cmd_string_ungetc(p); /* ch */ 301 302 buf = xrealloc(buf, 1, len + 1); 303 buf[len] = '\0'; 304 305 envent = environ_find(&global_environ, buf); 306 free(buf); 307 if (envent == NULL) 308 return (xstrdup("")); 309 return (xstrdup(envent->value)); 310 311 error: 312 free(buf); 313 return (NULL); 314 } 315 316 char * 317 cmd_string_expand_tilde(const char *s, size_t *p) 318 { 319 struct passwd *pw; 320 struct environ_entry *envent; 321 char *home, *path, *user, *cp; 322 int last; 323 324 home = NULL; 325 326 last = cmd_string_getc(s, p); 327 if (last == EOF || last == '/' || last == ' '|| last == '\t') { 328 envent = environ_find(&global_environ, "HOME"); 329 if (envent != NULL && *envent->value != '\0') 330 home = envent->value; 331 else if ((pw = getpwuid(getuid())) != NULL) 332 home = pw->pw_dir; 333 } else { 334 cmd_string_ungetc(p); 335 336 cp = user = xmalloc(strlen(s)); 337 for (;;) { 338 last = cmd_string_getc(s, p); 339 if (last == EOF || last == '/' || last == ' '|| last == '\t') 340 break; 341 *cp++ = last; 342 } 343 *cp = '\0'; 344 345 if ((pw = getpwnam(user)) != NULL) 346 home = pw->pw_dir; 347 free(user); 348 } 349 350 if (home == NULL) 351 return (NULL); 352 353 if (last != EOF) 354 xasprintf(&path, "%s%c", home, last); 355 else 356 xasprintf(&path, "%s", home); 357 return (path); 358 } 359