119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: v_increment.c,v 10.17 2011/12/02 01:17:53 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter
2819304Speter#include "../common/common.h"
2919304Speter#include "vi.h"
3019304Speter
31254225Speterstatic CHAR_T * const fmt[] = {
3219304Speter#define	DEC	0
33254225Speter	L("%ld"),
3419304Speter#define	SDEC	1
35254225Speter	L("%+ld"),
3619304Speter#define	HEXC	2
37254225Speter	L("0X%0*lX"),
3819304Speter#define	HEXL	3
39254225Speter	L("0x%0*lx"),
4019304Speter#define	OCTAL	4
41254225Speter	L("%#0*lo"),
4219304Speter};
4319304Speter
4419304Speterstatic void inc_err __P((SCR *, enum nresult));
4519304Speter
4619304Speter/*
4719304Speter * v_increment -- [count]#[#+-]
4819304Speter *	Increment/decrement a keyword number.
4919304Speter *
5019304Speter * PUBLIC: int v_increment __P((SCR *, VICMD *));
5119304Speter */
5219304Speterint
53254225Speterv_increment(SCR *sp, VICMD *vp)
5419304Speter{
5519304Speter	enum nresult nret;
5619304Speter	u_long ulval;
5719304Speter	long change, ltmp, lval;
5819304Speter	size_t beg, blen, end, len, nlen, wlen;
5919304Speter	int base, isempty, rval;
60254225Speter	CHAR_T *ntype, nbuf[100];
61254225Speter	CHAR_T *bp, *p, *t;
6219304Speter
6319304Speter	/* Validate the operator. */
6419304Speter	if (vp->character == '#')
6519304Speter		vp->character = '+';
6619304Speter	if (vp->character != '+' && vp->character != '-') {
6719304Speter		v_emsg(sp, vp->kp->usage, VIM_USAGE);
6819304Speter		return (1);
6919304Speter	}
7019304Speter
7119304Speter	/* If new value set, save it off, but it has to fit in a long. */
7219304Speter	if (F_ISSET(vp, VC_C1SET)) {
7319304Speter		if (vp->count > LONG_MAX) {
7419304Speter			inc_err(sp, NUM_OVER);
7519304Speter			return (1);
7619304Speter		}
7719304Speter		change = vp->count;
7819304Speter	} else
7919304Speter		change = 1;
8019304Speter
8119304Speter	/* Get the line. */
8219304Speter	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
8319304Speter		if (isempty)
8419304Speter			goto nonum;
8519304Speter		return (1);
8619304Speter	}
8719304Speter
8819304Speter	/*
8919304Speter	 * Skip any leading space before the number.  Getting a cursor word
9019304Speter	 * implies moving the cursor to its beginning, if we moved, refresh
9119304Speter	 * now.
9219304Speter	 */
93254225Speter	for (beg = vp->m_start.cno; beg < len && ISSPACE(p[beg]); ++beg);
9419304Speter	if (beg >= len)
9519304Speter		goto nonum;
9619304Speter	if (beg != vp->m_start.cno) {
9719304Speter		sp->cno = beg;
9819304Speter		(void)vs_refresh(sp, 0);
9919304Speter	}
10019304Speter
10119304Speter#undef	ishex
102254225Speter#define	ishex(c)	(ISXDIGIT(c))
10319304Speter#undef	isoctal
104254225Speter#define	isoctal(c)	((c) >= '0' && (c) <= '7')
10519304Speter
10619304Speter	/*
10719304Speter	 * Look for 0[Xx], or leading + or - signs, guess at the base.
10819304Speter	 * The character after that must be a number.  Wlen is set to
10919304Speter	 * the remaining characters in the line that could be part of
11019304Speter	 * the number.
11119304Speter	 */
11219304Speter	wlen = len - beg;
11319304Speter	if (p[beg] == '0' && wlen > 2 &&
11419304Speter	    (p[beg + 1] == 'X' || p[beg + 1] == 'x')) {
11519304Speter		base = 16;
11619304Speter		end = beg + 2;
11719304Speter		if (!ishex(p[end]))
11819304Speter			goto decimal;
11919304Speter		ntype = p[beg + 1] == 'X' ? fmt[HEXC] : fmt[HEXL];
12019304Speter	} else if (p[beg] == '0' && wlen > 1) {
12119304Speter		base = 8;
12219304Speter		end = beg + 1;
12319304Speter		if (!isoctal(p[end]))
12419304Speter			goto decimal;
12519304Speter		ntype = fmt[OCTAL];
12619304Speter	} else if (wlen >= 1 && (p[beg] == '+' || p[beg] == '-')) {
12719304Speter		base = 10;
12819304Speter		end = beg + 1;
12919304Speter		ntype = fmt[SDEC];
13019304Speter		if (!isdigit(p[end]))
13119304Speter			goto nonum;
13219304Speter	} else {
13319304Speterdecimal:	base = 10;
13419304Speter		end = beg;
13519304Speter		ntype = fmt[DEC];
13619304Speter		if (!isdigit(p[end])) {
13719304Speternonum:			msgq(sp, M_ERR, "181|Cursor not in a number");
13819304Speter			return (1);
13919304Speter		}
14019304Speter	}
14119304Speter
14219304Speter	/* Find the end of the word, possibly correcting the base. */
14319304Speter	while (++end < len) {
14419304Speter		switch (base) {
14519304Speter		case 8:
14619304Speter			if (isoctal(p[end]))
14719304Speter				continue;
14819304Speter			if (p[end] == '8' || p[end] == '9') {
14919304Speter				base = 10;
15019304Speter				ntype = fmt[DEC];
15119304Speter				continue;
15219304Speter			}
15319304Speter			break;
15419304Speter		case 10:
15519304Speter			if (isdigit(p[end]))
15619304Speter				continue;
15719304Speter			break;
15819304Speter		case 16:
15919304Speter			if (ishex(p[end]))
16019304Speter				continue;
16119304Speter			break;
16219304Speter		default:
16319304Speter			abort();
16419304Speter			/* NOTREACHED */
16519304Speter		}
16619304Speter		break;
16719304Speter	}
16819304Speter	wlen = (end - beg);
16919304Speter
17019304Speter	/*
17119304Speter	 * XXX
17219304Speter	 * If the line was at the end of the buffer, we have to copy it
17319304Speter	 * so we can guarantee that it's NULL-terminated.  We make the
17419304Speter	 * buffer big enough to fit the line changes as well, and only
17519304Speter	 * allocate once.
17619304Speter	 */
177254225Speter	GET_SPACE_RETW(sp, bp, blen, len + 50);
17819304Speter	if (end == len) {
179254225Speter		MEMMOVE(bp, &p[beg], wlen);
18019304Speter		bp[wlen] = '\0';
18119304Speter		t = bp;
18219304Speter	} else
18319304Speter		t = &p[beg];
18419304Speter
18519304Speter	/*
18619304Speter	 * Octal or hex deal in unsigned longs, everything else is done
18719304Speter	 * in signed longs.
18819304Speter	 */
18919304Speter	if (base == 10) {
19019304Speter		if ((nret = nget_slong(&lval, t, NULL, 10)) != NUM_OK)
19119304Speter			goto err;
19219304Speter		ltmp = vp->character == '-' ? -change : change;
19319304Speter		if (lval > 0 && ltmp > 0 && !NPFITS(LONG_MAX, lval, ltmp)) {
19419304Speter			nret = NUM_OVER;
19519304Speter			goto err;
19619304Speter		}
19719304Speter		if (lval < 0 && ltmp < 0 && !NNFITS(LONG_MIN, lval, ltmp)) {
19819304Speter			nret = NUM_UNDER;
19919304Speter			goto err;
20019304Speter		}
20119304Speter		lval += ltmp;
20219304Speter		/* If we cross 0, signed numbers lose their sign. */
20319304Speter		if (lval == 0 && ntype == fmt[SDEC])
20419304Speter			ntype = fmt[DEC];
205254225Speter		nlen = SPRINTF(nbuf, sizeof(nbuf), ntype, lval);
20619304Speter	} else {
20719304Speter		if ((nret = nget_uslong(&ulval, t, NULL, base)) != NUM_OK)
20819304Speter			goto err;
20919304Speter		if (vp->character == '+') {
21019304Speter			if (!NPFITS(ULONG_MAX, ulval, change)) {
21119304Speter				nret = NUM_OVER;
21219304Speter				goto err;
21319304Speter			}
21419304Speter			ulval += change;
21519304Speter		} else {
21619304Speter			if (ulval < change) {
21719304Speter				nret = NUM_UNDER;
21819304Speter				goto err;
21919304Speter			}
22019304Speter			ulval -= change;
22119304Speter		}
22219304Speter
22319304Speter		/* Correct for literal "0[Xx]" in format. */
22419304Speter		if (base == 16)
22519304Speter			wlen -= 2;
22619304Speter
227254225Speter		nlen = SPRINTF(nbuf, sizeof(nbuf), ntype, wlen, ulval);
22819304Speter	}
22919304Speter
23019304Speter	/* Build the new line. */
231254225Speter	MEMMOVE(bp, p, beg);
232254225Speter	MEMMOVE(bp + beg, nbuf, nlen);
233254225Speter	MEMMOVE(bp + beg + nlen, p + end, len - beg - (end - beg));
23419304Speter	len = beg + nlen + (len - beg - (end - beg));
23519304Speter
23619304Speter	nret = NUM_OK;
23719304Speter	rval = db_set(sp, vp->m_start.lno, bp, len);
23819304Speter
23919304Speter	if (0) {
24019304Spetererr:		rval = 1;
24119304Speter		inc_err(sp, nret);
24219304Speter	}
24319304Speter	if (bp != NULL)
244254225Speter		FREE_SPACEW(sp, bp, blen);
24519304Speter	return (rval);
24619304Speter}
24719304Speter
24819304Speterstatic void
249254225Speterinc_err(SCR *sp, enum nresult nret)
25019304Speter{
25119304Speter	switch (nret) {
25219304Speter	case NUM_ERR:
25319304Speter		break;
25419304Speter	case NUM_OK:
25519304Speter		abort();
25619304Speter		/* NOREACHED */
25719304Speter	case NUM_OVER:
25819304Speter		msgq(sp, M_ERR, "182|Resulting number too large");
25919304Speter		break;
26019304Speter	case NUM_UNDER:
26119304Speter		msgq(sp, M_ERR, "183|Resulting number too small");
26219304Speter		break;
26319304Speter	}
26419304Speter}
265