diff options
Diffstat (limited to 'tmux.c')
-rw-r--r-- | tmux.c | 165 |
1 files changed, 128 insertions, 37 deletions
@@ -26,6 +26,7 @@ #include <langinfo.h> #include <locale.h> #include <pwd.h> +#include <signal.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -46,15 +47,15 @@ const char *shell_command; static __dead void usage(void); static char *make_label(const char *, char **); +static int areshell(const char *); static const char *getshell(void); -static int checkshell(const char *); static __dead void usage(void) { fprintf(stderr, "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" - " [-S socket-path] [command [flags]]\n", + " [-S socket-path] [-T features] [command [flags]]\n", getprogname()); exit(1); } @@ -76,7 +77,7 @@ getshell(void) return (_PATH_BSHELL); } -static int +int checkshell(const char *shell) { if (shell == NULL || *shell != '/') @@ -88,7 +89,7 @@ checkshell(const char *shell) return (1); } -int +static int areshell(const char *shell) { const char *progname, *ptr; @@ -106,33 +107,103 @@ areshell(const char *shell) } static char * +expand_path(const char *path, const char *home) +{ + char *expanded, *name; + const char *end; + struct environ_entry *value; + + if (strncmp(path, "~/", 2) == 0) { + if (home == NULL) + return (NULL); + xasprintf(&expanded, "%s%s", home, path + 1); + return (expanded); + } + + if (*path == '$') { + end = strchr(path, '/'); + if (end == NULL) + name = xstrdup(path + 1); + else + name = xstrndup(path + 1, end - path - 1); + value = environ_find(global_environ, name); + free(name); + if (value == NULL) + return (NULL); + if (end == NULL) + end = ""; + xasprintf(&expanded, "%s%s", value->value, end); + return (expanded); + } + + return (xstrdup(path)); +} + +void +expand_paths(const char *s, char ***paths, u_int *n) +{ + const char *home = find_home(); + char *copy, *next, *tmp, resolved[PATH_MAX], *expanded; + u_int i; + + *paths = NULL; + *n = 0; + + copy = tmp = xstrdup(s); + while ((next = strsep(&tmp, ":")) != NULL) { + expanded = expand_path(next, home); + if (expanded == NULL) { + log_debug("%s: invalid path: %s", __func__, next); + continue; + } + if (realpath(expanded, resolved) == NULL) { + log_debug("%s: realpath(\"%s\") failed: %s", __func__, + expanded, strerror(errno)); + free(expanded); + continue; + } + free(expanded); + for (i = 0; i < *n; i++) { + if (strcmp(resolved, (*paths)[i]) == 0) + break; + } + if (i != *n) { + log_debug("%s: duplicate path: %s", __func__, resolved); + continue; + } + *paths = xreallocarray(*paths, (*n) + 1, sizeof *paths); + (*paths)[(*n)++] = xstrdup(resolved); + } + free(copy); +} + +static char * make_label(const char *label, char **cause) { - char *base, resolved[PATH_MAX], *path, *s; - struct stat sb; - uid_t uid; + char **paths, *path, *base; + u_int i, n; + struct stat sb; + uid_t uid; *cause = NULL; - if (label == NULL) label = "default"; uid = getuid(); - if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') - xasprintf(&base, "%s/tmux-%ld", s, (long)uid); - else - xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid); - if (realpath(base, resolved) == NULL && - strlcpy(resolved, base, sizeof resolved) >= sizeof resolved) { - errno = ERANGE; - free(base); - goto fail; + expand_paths(TMUX_SOCK, &paths, &n); + if (n == 0) { + xasprintf(cause, "no suitable socket path"); + return (NULL); } - free(base); + path = paths[0]; /* can only have one socket! */ + for (i = 1; i < n; i++) + free(paths[i]); + free(paths); - if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST) + xasprintf(&base, "%s/tmux-%ld", path, (long)uid); + if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) goto fail; - if (lstat(resolved, &sb) != 0) + if (lstat(base, &sb) != 0) goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; @@ -142,11 +213,13 @@ make_label(const char *label, char **cause) errno = EACCES; goto fail; } - xasprintf(&path, "%s/%s", resolved, label); + xasprintf(&path, "%s/%s", base, label); + free(base); return (path); fail: - xasprintf(cause, "error creating %s (%s)", resolved, strerror(errno)); + xasprintf(cause, "error creating %s (%s)", base, strerror(errno)); + free(base); return (NULL); } @@ -165,6 +238,19 @@ setblocking(int fd, int state) } const char * +sig2name(int signo) +{ + static char s[11]; + +#ifdef HAVE_SYS_SIGNAME + if (signo > 0 && signo < NSIG) + return (sys_signame[signo]); +#endif + xsnprintf(s, sizeof s, "%d", signo); + return (s); +} + +const char * find_cwd(void) { char resolved1[PATH_MAX], resolved2[PATH_MAX]; @@ -219,9 +305,11 @@ getversion(void) int main(int argc, char **argv) { - char *path, *label, *cause, **var; + char *path = NULL, *label = NULL; + char *cause, **var; const char *s, *shell, *cwd; - int opt, flags, keys; + int opt, flags = 0, keys; + int feat = 0; const struct options_table_entry *oe; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && @@ -238,14 +326,11 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; - else - flags = 0; - label = path = NULL; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:T:uUvV")) != -1) { switch (opt) { case '2': - flags |= CLIENT_256COLOURS; + tty_add_features(&feat, "256", ":,"); break; case 'c': shell_command = optarg; @@ -275,6 +360,9 @@ main(int argc, char **argv) free(path); path = xstrdup(optarg); break; + case 'T': + tty_add_features(&feat, optarg, ":,"); + break; case 'u': flags |= CLIENT_UTF8; break; @@ -321,9 +409,9 @@ main(int argc, char **argv) global_environ = environ_create(); for (var = environ; *var != NULL; var++) - environ_put(global_environ, *var); + environ_put(global_environ, *var, 0); if ((cwd = find_cwd()) != NULL) - environ_set(global_environ, "PWD", "%s", cwd); + environ_set(global_environ, "PWD", 0, "%s", cwd); global_options = options_create(NULL); global_s_options = options_create(NULL); @@ -368,16 +456,19 @@ main(int argc, char **argv) path[strcspn(path, ",")] = '\0'; } } - if (path == NULL && (path = make_label(label, &cause)) == NULL) { - if (cause != NULL) { - fprintf(stderr, "%s\n", cause); - free(cause); + if (path == NULL) { + if ((path = make_label(label, &cause)) == NULL) { + if (cause != NULL) { + fprintf(stderr, "%s\n", cause); + free(cause); + } + exit(1); } - exit(1); + flags |= CLIENT_DEFAULTSOCKET; } socket_path = path; free(label); /* Pass control to the client. */ - exit(client_main(osdep_event_init(), argc, argv, flags)); + exit(client_main(osdep_event_init(), argc, argv, flags, feat)); } |