Exemple #1
0
/**
 * getopt() used for shell built-in commands, the getopts command, and
 * command line options.
 * A leading ':' in options means don't print errors, instead return '?'
 * or ':' and set go->optarg to the offending option character.
 * If GF_ERROR is set (and option doesn't start with :), errors result in
 * a call to bi_errorf().
 *
 * Non-standard features:
 *	- ';' is like ':' in options, except the argument is optional
 *	  (if it isn't present, optarg is set to 0).
 *	  Used for 'set -o'.
 *	- ',' is like ':' in options, except the argument always immediately
 *	  follows the option character (optarg is set to the null string if
 *	  the option is missing).
 *	  Used for 'read -u2', 'print -u2' and fc -40.
 *	- '#' is like ':' in options, expect that the argument is optional
 *	  and must start with a digit. If the argument doesn't start with a
 *	  digit, it is assumed to be missing and normal option processing
 *	  continues (optarg is set to 0 if the option is missing).
 *	  Used for 'typeset -LZ4'.
 *	- accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
 *	  option starting with + is accepted, the GI_PLUS flag will be set
 *	  in go->info.
 */
int
ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
{
	char c;
	const char *o;

	if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
		const char *arg = argv[go->optind], flag = arg ? *arg : '\0';

		go->p = 1;
		if (flag == '-' && ksh_isdash(arg + 1)) {
			go->optind++;
			go->p = 0;
			go->info |= GI_MINUSMINUS;
			return (-1);
		}
		if (arg == NULL ||
		    ((flag != '-' ) &&
		    /* neither a - nor a + (if + allowed) */
		    (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
		    (c = arg[1]) == '\0') {
			go->p = 0;
			return (-1);
		}
		go->optind++;
		go->info &= ~(GI_MINUS|GI_PLUS);
		go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
	}
	go->p++;
	if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
	    !(o = cstrchr(optionsp, c))) {
		if (optionsp[0] == ':') {
			go->buf[0] = c;
			go->optarg = go->buf;
		} else {
			warningf(true, "%s%s-%c: %s",
			    (go->flags & GF_NONAME) ? "" : argv[0],
			    (go->flags & GF_NONAME) ? "" : ": ", c,
			    "unknown option");
			if (go->flags & GF_ERROR)
				bi_errorfz();
		}
		return ('?');
	}
	/**
	 * : means argument must be present, may be part of option argument
	 *   or the next argument
	 * ; same as : but argument may be missing
	 * , means argument is part of option argument, and may be null.
	 */
	if (*++o == ':' || *o == ';') {
		if (argv[go->optind - 1][go->p])
			go->optarg = argv[go->optind - 1] + go->p;
		else if (argv[go->optind])
			go->optarg = argv[go->optind++];
		else if (*o == ';')
			go->optarg = NULL;
		else {
			if (optionsp[0] == ':') {
				go->buf[0] = c;
				go->optarg = go->buf;
				return (':');
			}
			warningf(true, "%s%s-%c: %s",
			    (go->flags & GF_NONAME) ? "" : argv[0],
			    (go->flags & GF_NONAME) ? "" : ": ", c,
			    "requires an argument");
			if (go->flags & GF_ERROR)
				bi_errorfz();
			return ('?');
		}
		go->p = 0;
	} else if (*o == ',') {
		/* argument is attached to option character, even if null */
		go->optarg = argv[go->optind - 1] + go->p;
		go->p = 0;
	} else if (*o == '#') {
		/*
		 * argument is optional and may be attached or unattached
		 * but must start with a digit. optarg is set to 0 if the
		 * argument is missing.
		 */
		if (argv[go->optind - 1][go->p]) {
			if (ksh_isdigit(argv[go->optind - 1][go->p])) {
				go->optarg = argv[go->optind - 1] + go->p;
				go->p = 0;
			} else
				go->optarg = NULL;
		} else {
			if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) {
				go->optarg = argv[go->optind++];
				go->p = 0;
			} else
				go->optarg = NULL;
		}
	}
	return (c);
}
Exemple #2
0
int
c_cd(const char **wp)
{
	int optc, rv, phys_path;
	bool physical = tobool(Flag(FPHYSICAL));
	/* was a node from cdpath added in? */
	int cdnode;
	/* show where we went?, error for $PWD */
	bool printpath = false, eflag = false;
	struct tbl *pwd_s, *oldpwd_s;
	XString xs;
	char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;

	while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
		switch (optc) {
		case 'e':
			eflag = true;
			break;
		case 'L':
			physical = false;
			break;
		case 'P':
			physical = true;
			break;
		case '?':
			return (2);
		}
	wp += builtin_opt.optind;

	if (Flag(FRESTRICTED)) {
		bi_errorf("restricted shell - can't cd");
		return (2);
	}

	pwd_s = global("PWD");
	oldpwd_s = global("OLDPWD");

	if (!wp[0]) {
		/* No arguments - go home */
		if ((dir = str_val(global("HOME"))) == null) {
			bi_errorf("no home directory (HOME not set)");
			return (2);
		}
	} else if (!wp[1]) {
		/* One argument: - or dir */
		strdupx(allocd, wp[0], ATEMP);
		if (ksh_isdash((dir = allocd))) {
			afree(allocd, ATEMP);
			allocd = NULL;
			dir = str_val(oldpwd_s);
			if (dir == null) {
				bi_errorf("no OLDPWD");
				return (2);
			}
			printpath = true;
		}
	} else if (!wp[2]) {
		/* Two arguments - substitute arg1 in PWD for arg2 */
		size_t ilen, olen, nlen, elen;
		char *cp;

		if (!current_wd[0]) {
			bi_errorf("can't determine current directory");
			return (2);
		}
		/*
		 * substitute arg1 for arg2 in current path.
		 * if the first substitution fails because the cd fails
		 * we could try to find another substitution. For now
		 * we don't
		 */
		if ((cp = strstr(current_wd, wp[0])) == NULL) {
			bi_errorf("bad substitution");
			return (2);
		}
		/*-
		 * ilen = part of current_wd before wp[0]
		 * elen = part of current_wd after wp[0]
		 * because current_wd and wp[1] need to be in memory at the
		 * same time beforehand the addition can stay unchecked
		 */
		ilen = cp - current_wd;
		olen = strlen(wp[0]);
		nlen = strlen(wp[1]);
		elen = strlen(current_wd + ilen + olen) + 1;
		dir = allocd = alloc(ilen + nlen + elen, ATEMP);
		memcpy(dir, current_wd, ilen);
		memcpy(dir + ilen, wp[1], nlen);
		memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
		printpath = true;
	} else {
		bi_errorf("too many arguments");
		return (2);
	}

#ifdef MKSH__NO_PATH_MAX
	/* only a first guess; make_path will enlarge xs if necessary */
	XinitN(xs, 1024, ATEMP);
#else
	XinitN(xs, PATH_MAX, ATEMP);
#endif

	cdpath = str_val(global("CDPATH"));
	do {
		cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
		if (physical)
			rv = chdir(tryp = Xstring(xs, xp) + phys_path);
		else {
			simplify_path(Xstring(xs, xp));
			rv = chdir(tryp = Xstring(xs, xp));
		}
	} while (rv < 0 && cdpath != NULL);

	if (rv < 0) {
		if (cdnode)
			bi_errorf("%s: %s", dir, "bad directory");
		else
			bi_errorf("%s: %s", tryp, cstrerror(errno));
		afree(allocd, ATEMP);
		Xfree(xs, xp);
		return (2);
	}

	rv = 0;

	/* allocd (above) => dir, which is no longer used */
	afree(allocd, ATEMP);
	allocd = NULL;

	/* Clear out tracked aliases with relative paths */
	flushcom(false);

	/*
	 * Set OLDPWD (note: unsetting OLDPWD does not disable this
	 * setting in AT&T ksh)
	 */
	if (current_wd[0])
		/* Ignore failure (happens if readonly or integer) */
		setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);

	if (!mksh_abspath(Xstring(xs, xp))) {
		pwd = NULL;
	} else if (!physical) {
		goto norealpath_PWD;
	} else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
		if (eflag)
			rv = 1;
 norealpath_PWD:
		pwd = Xstring(xs, xp);
	}

	/* Set PWD */
	if (pwd) {
		char *ptmp = pwd;

		set_current_wd(ptmp);
		/* Ignore failure (happens if readonly or integer) */
		setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
	} else {
		set_current_wd(null);
		pwd = Xstring(xs, xp);
		/* XXX unset $PWD? */
		if (eflag)
			rv = 1;
	}
	if (printpath || cdnode)
		shprintf("%s\n", pwd);

	afree(allocd, ATEMP);
	Xfree(xs, xp);
	return (rv);
}
Exemple #3
0
/*
 * set up redirection, saving old fds in e->savefd
 */
static int
iosetup(struct ioword *iop, struct tbl *tp)
{
	int u = -1;
	char *cp = iop->ioname;
	int iotype = iop->ioflag & IOTYPE;
	bool do_open = true, do_close = false;
	int flags = 0;
	struct ioword iotmp;
	struct stat statb;

	if (iotype != IOHERE)
		cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0));

	/* Used for tracing and error messages to print expanded cp */
	iotmp = *iop;
	iotmp.ioname = (iotype == IOHERE) ? NULL : cp;
	iotmp.ioflag |= IONAMEXP;

	if (Flag(FXTRACE)) {
		change_xtrace(2, false);
		fptreef(shl_xtrace, 0, "%R", &iotmp);
		change_xtrace(1, false);
	}

	switch (iotype) {
	case IOREAD:
		flags = O_RDONLY;
		break;

	case IOCAT:
		flags = O_WRONLY | O_APPEND | O_CREAT;
		break;

	case IOWRITE:
		flags = O_WRONLY | O_CREAT | O_TRUNC;
		/*
		 * The stat() is here to allow redirections to
		 * things like /dev/null without error.
		 */
		if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB) &&
		    (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
			flags |= O_EXCL;
		break;

	case IORDWR:
		flags = O_RDWR | O_CREAT;
		break;

	case IOHERE:
		do_open = false;
		/* herein() returns -2 if error has been printed */
		u = herein(iop, NULL);
		/* cp may have wrong name */
		break;

	case IODUP: {
		const char *emsg;

		do_open = false;
		if (ksh_isdash(cp)) {
			/* prevent error return below */
			u = 1009;
			do_close = true;
		} else if ((u = check_fd(cp,
		    X_OK | ((iop->ioflag & IORDUP) ? R_OK : W_OK),
		    &emsg)) < 0) {
			char *sp;

			warningf(true, "%s: %s",
			    (sp = snptreef(NULL, 32, "%R", &iotmp)), emsg);
			afree(sp, ATEMP);
			return (-1);
		}
		if (u == (int)iop->unit)
			/* "dup from" == "dup to" */
			return (0);
		break;
	    }
	}

	if (do_open) {
		if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
			warningf(true, "%s: %s", cp, "restricted");
			return (-1);
		}
		u = binopen3(cp, flags, 0666);
	}
	if (u < 0) {
		/* herein() may already have printed message */
		if (u == -1) {
			u = errno;
			warningf(true, "can't %s %s: %s",
			    iotype == IODUP ? "dup" :
			    (iotype == IOREAD || iotype == IOHERE) ?
			    "open" : "create", cp, cstrerror(u));
		}
		return (-1);
	}
	/* Do not save if it has already been redirected (i.e. "cat >x >y"). */
	if (e->savefd[iop->unit] == 0) {
		/* If these are the same, it means unit was previously closed */
		if (u == (int)iop->unit)
			e->savefd[iop->unit] = -1;
		else
			/*
			 * c_exec() assumes e->savefd[fd] set for any
			 * redirections. Ask savefd() not to close iop->unit;
			 * this allows error messages to be seen if iop->unit
			 * is 2; also means we can't lose the fd (eg, both
			 * dup2 below and dup2 in restfd() failing).
			 */
			e->savefd[iop->unit] = savefd(iop->unit);
	}

	if (do_close)
		close(iop->unit);
	else if (u != (int)iop->unit) {
		if (ksh_dup2(u, iop->unit, true) < 0) {
			int eno;
			char *sp;

			eno = errno;
			warningf(true, "%s %s: %s",
			    "can't finish (dup) redirection",
			    (sp = snptreef(NULL, 32, "%R", &iotmp)),
			    cstrerror(eno));
			afree(sp, ATEMP);
			if (iotype != IODUP)
				close(u);
			return (-1);
		}
		if (iotype != IODUP)
			close(u);
		/*
		 * Touching any co-process fd in an empty exec
		 * causes the shell to close its copies
		 */
		else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
			if (iop->ioflag & IORDUP)
				/* possible exec <&p */
				coproc_read_close(u);
			else
				/* possible exec >&p */
				coproc_write_close(u);
		}
	}
	if (u == 2)
		/* Clear any write errors */
		shf_reopen(2, SHF_WR, shl_out);
	return (0);
}