Exemple #1
0
/*
 * opts_set --
 *	Change the values of one or more options.
 *
 * PUBLIC: int opts_set __P((SCR *, ARGS *[], const char *));
 */
int
opts_set(SCR *sp, ARGS **argv, const char *usage)
{
	enum optdisp disp;
	enum nresult nret;
	OPTLIST const *op;
	OPTION *spo;
	u_long isset, turnoff, value;
	int ch, equals, nf, nf2, offset, qmark, rval;
	CHAR_T *endp, *name, *p, *sep;
	char *p2, *t2;
	const char *np;
	size_t nlen;

	disp = NO_DISPLAY;
	for (rval = 0; argv[0]->len != 0; ++argv) {
		/*
		 * The historic vi dumped the options for each occurrence of
		 * "all" in the set list.  Puhleeze.
		 */
		if (!STRCMP(argv[0]->bp, L("all"))) {
			disp = ALL_DISPLAY;
			continue;
		}

		/* Find equals sign or question mark. */
		for (sep = NULL, equals = qmark = 0,
		    p = name = argv[0]->bp; (ch = *p) != '\0'; ++p)
			if (ch == '=' || ch == '?') {
				if (p == name) {
					if (usage != NULL)
						msgq(sp, M_ERR,
						    "032|Usage: %s", usage);
					return (1);
				}
				sep = p;
				if (ch == '=')
					equals = 1;
				else
					qmark = 1;
				break;
			}

		turnoff = 0;
		op = NULL;
		if (sep != NULL)
			*sep++ = '\0';

		/* Search for the name, then name without any leading "no". */
		if ((op = opts_search(name)) == NULL &&
		    name[0] == L('n') && name[1] == L('o')) {
			turnoff = 1;
			name += 2;
			op = opts_search(name);
		}
		if (op == NULL) {
			opts_nomatch(sp, name);
			rval = 1;
			continue;
		}

		/* Find current option values. */
		offset = op - optlist;
		spo = sp->opts + offset;

		/*
		 * !!!
		 * Historically, the question mark could be a separate
		 * argument.
		 */
		if (!equals && !qmark &&
		    argv[1]->len == 1 && argv[1]->bp[0] == '?') {
			++argv;
			qmark = 1;
		}

		/* Set name, value. */
		switch (op->type) {
		case OPT_0BOOL:
		case OPT_1BOOL:
			/* Some options may not be reset. */
			if (F_ISSET(op, OPT_NOUNSET) && turnoff) {
				msgq_wstr(sp, M_ERR, name,
			    "291|set: the %s option may not be turned off");
				rval = 1;
				break;
			}

			/* Some options may not be set. */
			if (F_ISSET(op, OPT_NOSET) && !turnoff) {
				msgq_wstr(sp, M_ERR, name,
			    "313|set: the %s option may never be turned on");
				rval = 1;
				break;
			}

			if (equals) {
				msgq_wstr(sp, M_ERR, name,
			    "034|set: [no]%s option doesn't take a value");
				rval = 1;
				break;
			}
			if (qmark) {
				if (!disp)
					disp = SELECT_DISPLAY;
				F_SET(spo, OPT_SELECTED);
				break;
			}

			/*
			 * Do nothing if the value is unchanged, the underlying
			 * functions can be expensive.
			 */
			isset = !turnoff;
			if (!F_ISSET(op, OPT_ALWAYS)) {
				if (isset) {
					if (O_ISSET(sp, offset))
						break;
				} else
					if (!O_ISSET(sp, offset))
						break;
			}

			/* Report to subsystems. */
			if ((op->func != NULL &&
			    op->func(sp, spo, NULL, &isset)) ||
			    ex_optchange(sp, offset, NULL, &isset) ||
			    v_optchange(sp, offset, NULL, &isset) ||
			    sp->gp->scr_optchange(sp, offset, NULL, &isset)) {
				rval = 1;
				break;
			}

			/* Set the value. */
			if (isset)
				O_SET(sp, offset);
			else
				O_CLR(sp, offset);
			break;
		case OPT_NUM:
			if (turnoff) {
				msgq_wstr(sp, M_ERR, name,
				    "035|set: %s option isn't a boolean");
				rval = 1;
				break;
			}
			if (qmark || !equals) {
				if (!disp)
					disp = SELECT_DISPLAY;
				F_SET(spo, OPT_SELECTED);
				break;
			}

			if (!ISDIGIT((UCHAR_T)sep[0]))
				goto badnum;
			if ((nret =
			    nget_uslong(sp, &value, sep, &endp, 10)) != NUM_OK) {
				INT2CHAR(sp, name, STRLEN(name) + 1, 
					     np, nlen);
				p2 = msg_print(sp, np, &nf);
				INT2CHAR(sp, sep, STRLEN(sep) + 1, 
					     np, nlen);
				t2 = msg_print(sp, np, &nf2);
				switch (nret) {
				case NUM_ERR:
					msgq(sp, M_SYSERR,
					    "036|set: %s option: %s", p2, t2);
					break;
				case NUM_OVER:
					msgq(sp, M_ERR,
			    "037|set: %s option: %s: value overflow", p2, t2);
					break;
				case NUM_OK:
				case NUM_UNDER:
					abort();
				}
				if (nf)
					FREE_SPACE(sp, p2, 0);
				if (nf2)
					FREE_SPACE(sp, t2, 0);
				rval = 1;
				break;
			}
			if (*endp && !ISBLANK(*endp)) {
badnum:				INT2CHAR(sp, name, STRLEN(name) + 1, 
					     np, nlen);
				p2 = msg_print(sp, np, &nf);
				INT2CHAR(sp, sep, STRLEN(sep) + 1, 
					     np, nlen);
				t2 = msg_print(sp, np, &nf2);
				msgq(sp, M_ERR,
		    "038|set: %s option: %s is an illegal number", p2, t2);
				if (nf)
					FREE_SPACE(sp, p2, 0);
				if (nf2)
					FREE_SPACE(sp, t2, 0);
				rval = 1;
				break;
			}

			/* Some options may never be set to zero. */
			if (F_ISSET(op, OPT_NOZERO) && value == 0) {
				msgq_wstr(sp, M_ERR, name,
			    "314|set: the %s option may never be set to 0");
				rval = 1;
				break;
			}

			/*
			 * Do nothing if the value is unchanged, the underlying
			 * functions can be expensive.
			 */
			if (!F_ISSET(op, OPT_ALWAYS) &&
			    O_VAL(sp, offset) == value)
				break;

			/* Report to subsystems. */
			INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen);
			if ((op->func != NULL &&
			    op->func(sp, spo, np, &value)) ||
			    ex_optchange(sp, offset, np, &value) ||
			    v_optchange(sp, offset, np, &value) ||
			    sp->gp->scr_optchange(sp, offset, np, &value)) {
				rval = 1;
				break;
			}

			/* Set the value. */
			if (o_set(sp, offset, 0, NULL, value))
				rval = 1;
			break;
		case OPT_STR:
			if (turnoff) {
				msgq_wstr(sp, M_ERR, name,
				    "039|set: %s option isn't a boolean");
				rval = 1;
				break;
			}
			if (qmark || !equals) {
				if (!disp)
					disp = SELECT_DISPLAY;
				F_SET(spo, OPT_SELECTED);
				break;
			}

			/* Check for strings that must have even length */
			if (F_ISSET(op, OPT_PAIRS) && STRLEN(sep) & 1) {
				msgq_wstr(sp, M_ERR, name,
				    "047|set: the %s option must be in two character groups");
				rval = 1;
				break;
			}

			/*
			 * Do nothing if the value is unchanged, the underlying
			 * functions can be expensive.
			 */
			INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen);
			if (!F_ISSET(op, OPT_ALWAYS) &&
			    O_STR(sp, offset) != NULL &&
			    !strcmp(O_STR(sp, offset), np))
				break;

			/* Report to subsystems. */
			if ((op->func != NULL &&
			    op->func(sp, spo, np, NULL)) ||
			    ex_optchange(sp, offset, np, NULL) ||
			    v_optchange(sp, offset, np, NULL) ||
			    sp->gp->scr_optchange(sp, offset, np, NULL)) {
				rval = 1;
				break;
			}

			/* Set the value. */
			if (o_set(sp, offset, OS_STRDUP, np, 0))
				rval = 1;
			break;
		default:
			abort();
		}
	}
	if (disp != NO_DISPLAY)
		opts_dump(sp, disp);
	return (rval);
}
Exemple #2
0
/*
 * v_increment -- [count]#[#+-]
 *	Increment/decrement a keyword number.
 *
 * PUBLIC: int v_increment __P((SCR *, VICMD *));
 */
int
v_increment(SCR *sp, VICMD *vp)
{
	enum nresult nret;
	u_long ulval;
	long change, ltmp, lval;
	size_t beg, blen, end, len, nlen, wlen;
	int base, isempty, rval;
	CHAR_T *ntype, nbuf[100];
	CHAR_T *bp, *p, *t;

	/* Validate the operator. */
	if (vp->character == '#')
		vp->character = '+';
	if (vp->character != '+' && vp->character != '-') {
		v_emsg(sp, vp->kp->usage, VIM_USAGE);
		return (1);
	}

	/* If new value set, save it off, but it has to fit in a long. */
	if (F_ISSET(vp, VC_C1SET)) {
		if (vp->count > LONG_MAX) {
			inc_err(sp, NUM_OVER);
			return (1);
		}
		change = vp->count;
	} else
		change = 1;

	/* Get the line. */
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto nonum;
		return (1);
	}

	/*
	 * Skip any leading space before the number.  Getting a cursor word
	 * implies moving the cursor to its beginning, if we moved, refresh
	 * now.
	 */
	for (beg = vp->m_start.cno; beg < len && ISSPACE(p[beg]); ++beg);
	if (beg >= len)
		goto nonum;
	if (beg != vp->m_start.cno) {
		sp->cno = beg;
		(void)vs_refresh(sp, 0);
	}

#undef	ishex
#define	ishex(c)	(ISXDIGIT(c))
#undef	isoctal
#define	isoctal(c)	((c) >= '0' && (c) <= '7')

	/*
	 * Look for 0[Xx], or leading + or - signs, guess at the base.
	 * The character after that must be a number.  Wlen is set to
	 * the remaining characters in the line that could be part of
	 * the number.
	 */
	wlen = len - beg;
	if (p[beg] == '0' && wlen > 2 &&
	    (p[beg + 1] == 'X' || p[beg + 1] == 'x')) {
		base = 16;
		end = beg + 2;
		if (!ishex(p[end]))
			goto decimal;
		ntype = p[beg + 1] == 'X' ? fmt[HEXC] : fmt[HEXL];
	} else if (p[beg] == '0' && wlen > 1) {
		base = 8;
		end = beg + 1;
		if (!isoctal(p[end]))
			goto decimal;
		ntype = fmt[OCTAL];
	} else if (wlen >= 1 && (p[beg] == '+' || p[beg] == '-')) {
		base = 10;
		end = beg + 1;
		ntype = fmt[SDEC];
		if (!isdigit(p[end]))
			goto nonum;
	} else {
decimal:	base = 10;
		end = beg;
		ntype = fmt[DEC];
		if (!isdigit(p[end])) {
nonum:			msgq(sp, M_ERR, "181|Cursor not in a number");
			return (1);
		}
	}

	/* Find the end of the word, possibly correcting the base. */
	while (++end < len) {
		switch (base) {
		case 8:
			if (isoctal(p[end]))
				continue;
			if (p[end] == '8' || p[end] == '9') {
				base = 10;
				ntype = fmt[DEC];
				continue;
			}
			break;
		case 10:
			if (isdigit(p[end]))
				continue;
			break;
		case 16:
			if (ishex(p[end]))
				continue;
			break;
		default:
			abort();
			/* NOTREACHED */
		}
		break;
	}
	wlen = (end - beg);

	/*
	 * XXX
	 * If the line was at the end of the buffer, we have to copy it
	 * so we can guarantee that it's NULL-terminated.  We make the
	 * buffer big enough to fit the line changes as well, and only
	 * allocate once.
	 */
	GET_SPACE_RETW(sp, bp, blen, len + 50);
	if (end == len) {
		MEMMOVE(bp, &p[beg], wlen);
		bp[wlen] = '\0';
		t = bp;
	} else
		t = &p[beg];

	/*
	 * Octal or hex deal in unsigned longs, everything else is done
	 * in signed longs.
	 */
	if (base == 10) {
		if ((nret = nget_slong(&lval, t, NULL, 10)) != NUM_OK)
			goto err;
		ltmp = vp->character == '-' ? -change : change;
		if (lval > 0 && ltmp > 0 && !NPFITS(LONG_MAX, lval, ltmp)) {
			nret = NUM_OVER;
			goto err;
		}
		if (lval < 0 && ltmp < 0 && !NNFITS(LONG_MIN, lval, ltmp)) {
			nret = NUM_UNDER;
			goto err;
		}
		lval += ltmp;
		/* If we cross 0, signed numbers lose their sign. */
		if (lval == 0 && ntype == fmt[SDEC])
			ntype = fmt[DEC];
		nlen = SPRINTF(nbuf, sizeof(nbuf), ntype, lval);
	} else {
		if ((nret = nget_uslong(&ulval, t, NULL, base)) != NUM_OK)
			goto err;
		if (vp->character == '+') {
			if (!NPFITS(ULONG_MAX, ulval, change)) {
				nret = NUM_OVER;
				goto err;
			}
			ulval += change;
		} else {
			if (ulval < change) {
				nret = NUM_UNDER;
				goto err;
			}
			ulval -= change;
		}

		/* Correct for literal "0[Xx]" in format. */
		if (base == 16)
			wlen -= 2;

		nlen = SPRINTF(nbuf, sizeof(nbuf), ntype, wlen, ulval);
	}

	/* Build the new line. */
	MEMMOVE(bp, p, beg);
	MEMMOVE(bp + beg, nbuf, nlen);
	MEMMOVE(bp + beg + nlen, p + end, len - beg - (end - beg));
	len = beg + nlen + (len - beg - (end - beg));

	nret = NUM_OK;
	rval = db_set(sp, vp->m_start.lno, bp, len);

	if (0) {
err:		rval = 1;
		inc_err(sp, nret);
	}
	if (bp != NULL)
		FREE_SPACEW(sp, bp, blen);
	return (rval);
}