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