[BACK]Return to cmd-string.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

File: [local] / src / usr.bin / tmux / Attic / cmd-string.c (download)

Revision 1.31, Fri Apr 26 11:38:51 2019 UTC (5 years, 1 month ago) by nicm
Branch: MAIN
Changes since 1.30: +5 -2 lines

Merge hooks into options and make each one an array option. This allows
multiple commands to be easily bound to one hook. set-hook and
show-hooks remain but they are now variants of set-option and
show-options. show-options now has a -H flag to show hooks (by default
they are not shown).

/* $OpenBSD: cmd-string.c,v 1.31 2019/04/26 11:38:51 nicm Exp $ */

/*
 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>

#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "tmux.h"

/*
 * Parse a command from a string.
 */

static int	 cmd_string_getc(const char *, size_t *);
static void	 cmd_string_ungetc(size_t *);
static void	 cmd_string_copy(char **, char *, size_t *);
static char	*cmd_string_string(const char *, size_t *, char, int);
static char	*cmd_string_variable(const char *, size_t *);
static char	*cmd_string_expand_tilde(const char *, size_t *);

static int
cmd_string_getc(const char *s, size_t *p)
{
	const u_char	*ucs = s;

	if (ucs[*p] == '\0')
		return (EOF);
	return (ucs[(*p)++]);
}

static void
cmd_string_ungetc(size_t *p)
{
	(*p)--;
}

static int
cmd_string_unicode(wchar_t *wc, const char *s, size_t *p, char ch)
{
	int	size = (ch == 'u') ? 4 : 8;
	u_int	tmp;

	if (size == 4 && sscanf(s + *p, "%4x", &tmp) != 1)
		return (-1);
	if (size == 8 && sscanf(s + *p, "%8x", &tmp) != 1)
		return (-1);
	*p += size;

	*wc = (wchar_t)tmp;
	return (0);
}

int
cmd_string_split(const char *s, int *rargc, char ***rargv)
{
	size_t		p = 0;
	int		ch, argc = 0, append = 0;
	char	      **argv = NULL, *buf = NULL, *t;
	const char     *whitespace, *equals;
	size_t		len = 0;

	for (;;) {
		ch = cmd_string_getc(s, &p);
		switch (ch) {
		case '\'':
			if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
				goto error;
			cmd_string_copy(&buf, t, &len);
			break;
		case '"':
			if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
				goto error;
			cmd_string_copy(&buf, t, &len);
			break;
		case '$':
			if ((t = cmd_string_variable(s, &p)) == NULL)
				goto error;
			cmd_string_copy(&buf, t, &len);
			break;
		case '#':
			/* Comment: discard rest of line. */
			while ((ch = cmd_string_getc(s, &p)) != EOF)
				;
			/* FALLTHROUGH */
		case EOF:
		case ' ':
		case '\t':
			if (buf != NULL) {
				buf = xrealloc(buf, len + 1);
				buf[len] = '\0';

				argv = xreallocarray(argv, argc + 1,
				    sizeof *argv);
				argv[argc++] = buf;

				buf = NULL;
				len = 0;
			}

			if (ch != EOF)
				break;

			while (argc != 0) {
				equals = strchr(argv[0], '=');
				whitespace = argv[0] + strcspn(argv[0], " \t");
				if (equals == NULL || equals > whitespace)
					break;
				environ_put(global_environ, argv[0]);
				argc--;
				memmove(argv, argv + 1, argc * (sizeof *argv));
			}
			goto done;
		case '~':
			if (buf != NULL) {
				append = 1;
				break;
			}
			t = cmd_string_expand_tilde(s, &p);
			if (t == NULL)
				goto error;
			cmd_string_copy(&buf, t, &len);
			break;
		default:
			append = 1;
			break;
		}
		if (append) {
			if (len >= SIZE_MAX - 2)
				goto error;
			buf = xrealloc(buf, len + 1);
			buf[len++] = ch;
		}
		append = 0;
	}

done:
	*rargc = argc;
	*rargv = argv;

	free(buf);
	return (0);

error:
	if (argv != NULL)
		cmd_free_argv(argc, argv);
	free(buf);
	return (-1);
}

struct cmd_list *
cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
{
	struct cmd_list	 *cmdlist = NULL;
	int		  argc;
	char		**argv;

	if (cause != NULL)
		*cause = NULL;
	log_debug ("%s: %s", __func__, s);

	if (cmd_string_split(s, &argc, &argv) != 0) {
		xasprintf(cause, "invalid or unknown command: %s", s);
		return (NULL);
	}
	if (argc != 0) {
		cmdlist = cmd_list_parse(argc, argv, file, line, cause);
		if (cmdlist == NULL) {
			cmd_free_argv(argc, argv);
			return (NULL);
		}
	}
	cmd_free_argv(argc, argv);
	return (cmdlist);
}

static void
cmd_string_copy(char **dst, char *src, size_t *len)
{
	size_t srclen;

	srclen = strlen(src);

	*dst = xrealloc(*dst, *len + srclen + 1);
	strlcpy(*dst + *len, src, srclen + 1);

	*len += srclen;
	free(src);
}

static char *
cmd_string_string(const char *s, size_t *p, char endch, int esc)
{
	int			ch;
	wchar_t			wc;
	struct utf8_data	ud;
	char		       *buf = NULL, *t;
	size_t			len = 0;

	while ((ch = cmd_string_getc(s, p)) != endch) {
		switch (ch) {
		case EOF:
			goto error;
		case '\\':
			if (!esc)
				break;
			switch (ch = cmd_string_getc(s, p)) {
			case EOF:
				goto error;
			case 'e':
				ch = '\033';
				break;
			case 'r':
				ch = '\r';
				break;
			case 'n':
				ch = '\n';
				break;
			case 't':
				ch = '\t';
				break;
			case 'u':
			case 'U':
				if (cmd_string_unicode(&wc, s, p, ch) != 0)
					goto error;
				if (utf8_split(wc, &ud) != UTF8_DONE)
					goto error;
				if (len >= SIZE_MAX - ud.size - 1)
					goto error;
				buf = xrealloc(buf, len + ud.size);
				memcpy(buf + len, ud.data, ud.size);
				len += ud.size;
				continue;
			}
			break;
		case '$':
			if (!esc)
				break;
			if ((t = cmd_string_variable(s, p)) == NULL)
				goto error;
			cmd_string_copy(&buf, t, &len);
			continue;
		}

		if (len >= SIZE_MAX - 2)
			goto error;
		buf = xrealloc(buf, len + 1);
		buf[len++] = ch;
	}

	buf = xrealloc(buf, len + 1);
	buf[len] = '\0';
	return (buf);

error:
	free(buf);
	return (NULL);
}

static char *
cmd_string_variable(const char *s, size_t *p)
{
	int			ch, fch;
	char		       *buf, *t;
	size_t			len;
	struct environ_entry   *envent;

#define cmd_string_first(ch) ((ch) == '_' || \
	((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
#define cmd_string_other(ch) ((ch) == '_' || \
	((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
	((ch) >= '0' && (ch) <= '9'))

	buf = NULL;
	len = 0;

	fch = EOF;
	switch (ch = cmd_string_getc(s, p)) {
	case EOF:
		goto error;
	case '{':
		fch = '{';

		ch = cmd_string_getc(s, p);
		if (!cmd_string_first(ch))
			goto error;
		/* FALLTHROUGH */
	default:
		if (!cmd_string_first(ch)) {
			xasprintf(&t, "$%c", ch);
			return (t);
		}

		buf = xrealloc(buf, len + 1);
		buf[len++] = ch;

		for (;;) {
			ch = cmd_string_getc(s, p);
			if (ch == EOF || !cmd_string_other(ch))
				break;
			else {
				if (len >= SIZE_MAX - 3)
					goto error;
				buf = xrealloc(buf, len + 1);
				buf[len++] = ch;
			}
		}
	}

	if (fch == '{' && ch != '}')
		goto error;
	if (ch != EOF && fch != '{')
		cmd_string_ungetc(p); /* ch */

	buf = xrealloc(buf, len + 1);
	buf[len] = '\0';

	envent = environ_find(global_environ, buf);
	free(buf);
	if (envent == NULL)
		return (xstrdup(""));
	return (xstrdup(envent->value));

error:
	free(buf);
	return (NULL);
}

static char *
cmd_string_expand_tilde(const char *s, size_t *p)
{
	struct passwd		*pw;
	struct environ_entry	*envent;
	char			*home, *path, *user, *cp;
	int			 last;

	home = NULL;

	last = cmd_string_getc(s, p);
	if (last == EOF || last == '/' || last == ' '|| last == '\t') {
		envent = environ_find(global_environ, "HOME");
		if (envent != NULL && *envent->value != '\0')
			home = envent->value;
		else if ((pw = getpwuid(getuid())) != NULL)
			home = pw->pw_dir;
	} else {
		cmd_string_ungetc(p);

		cp = user = xmalloc(strlen(s));
		for (;;) {
			last = cmd_string_getc(s, p);
			if (last == EOF ||
			    last == '/' ||
			    last == ' '||
			    last == '\t')
				break;
			*cp++ = last;
		}
		*cp = '\0';

		if ((pw = getpwnam(user)) != NULL)
			home = pw->pw_dir;
		free(user);
	}

	if (home == NULL)
		return (NULL);

	if (last != EOF)
		xasprintf(&path, "%s%c", home, last);
	else
		xasprintf(&path, "%s", home);
	return (path);
}