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