GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../vi/v_increment.c Lines: 0 95 0.0 %
Date: 2017-11-07 Branches: 0 132 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: v_increment.c,v 1.9 2016/01/06 22:28:52 millert Exp $	*/
2
3
/*-
4
 * Copyright (c) 1992, 1993, 1994
5
 *	The Regents of the University of California.  All rights reserved.
6
 * Copyright (c) 1992, 1993, 1994, 1995, 1996
7
 *	Keith Bostic.  All rights reserved.
8
 *
9
 * See the LICENSE file for redistribution information.
10
 */
11
12
#include "config.h"
13
14
#include <sys/types.h>
15
#include <sys/queue.h>
16
#include <sys/time.h>
17
18
#include <bitstring.h>
19
#include <ctype.h>
20
#include <errno.h>
21
#include <limits.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "../common/common.h"
27
#include "vi.h"
28
29
static char * const fmt[] = {
30
#define	DEC	0
31
	"%ld",
32
#define	SDEC	1
33
	"%+ld",
34
#define	HEXC	2
35
	"0X%0*lX",
36
#define	HEXL	3
37
	"0x%0*lx",
38
#define	OCTAL	4
39
	"%#0*lo",
40
};
41
42
static void inc_err(SCR *, enum nresult);
43
44
/*
45
 * v_increment -- [count]#[#+-]
46
 *	Increment/decrement a keyword number.
47
 *
48
 * PUBLIC: int v_increment(SCR *, VICMD *);
49
 */
50
int
51
v_increment(SCR *sp, VICMD *vp)
52
{
53
	enum nresult nret;
54
	u_long ulval;
55
	long change, ltmp, lval;
56
	size_t beg, blen, end, len, nlen, wlen;
57
	int base, isempty, rval;
58
	char *bp, *ntype, *p, *t, nbuf[100];
59
60
	/* Validate the operator. */
61
	if (vp->character == '#')
62
		vp->character = '+';
63
	if (vp->character != '+' && vp->character != '-') {
64
		v_emsg(sp, vp->kp->usage, VIM_USAGE);
65
		return (1);
66
	}
67
68
	/* If new value set, save it off, but it has to fit in a long. */
69
	if (F_ISSET(vp, VC_C1SET)) {
70
		if (vp->count > LONG_MAX) {
71
			inc_err(sp, NUM_OVER);
72
			return (1);
73
		}
74
		change = vp->count;
75
	} else
76
		change = 1;
77
78
	/* Get the line. */
79
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
80
		if (isempty)
81
			goto nonum;
82
		return (1);
83
	}
84
85
	/*
86
	 * Skip any leading space before the number.  Getting a cursor word
87
	 * implies moving the cursor to its beginning, if we moved, refresh
88
	 * now.
89
	 */
90
	for (beg = vp->m_start.cno; beg < len && isspace(p[beg]); ++beg);
91
	if (beg >= len)
92
		goto nonum;
93
	if (beg != vp->m_start.cno) {
94
		sp->cno = beg;
95
		(void)vs_refresh(sp, 0);
96
	}
97
98
#undef	ishex
99
#define	ishex(c)	(isdigit(c) || strchr("abcdefABCDEF", (c)))
100
#undef	isoctal
101
#define	isoctal(c)	(isdigit(c) && (c) != '8' && (c) != '9')
102
103
	/*
104
	 * Look for 0[Xx], or leading + or - signs, guess at the base.
105
	 * The character after that must be a number.  Wlen is set to
106
	 * the remaining characters in the line that could be part of
107
	 * the number.
108
	 */
109
	wlen = len - beg;
110
	if (p[beg] == '0' && wlen > 2 &&
111
	    (p[beg + 1] == 'X' || p[beg + 1] == 'x')) {
112
		base = 16;
113
		end = beg + 2;
114
		if (!ishex(p[end]))
115
			goto decimal;
116
		ntype = p[beg + 1] == 'X' ? fmt[HEXC] : fmt[HEXL];
117
	} else if (p[beg] == '0' && wlen > 1) {
118
		base = 8;
119
		end = beg + 1;
120
		if (!isoctal(p[end]))
121
			goto decimal;
122
		ntype = fmt[OCTAL];
123
	} else if (wlen >= 1 && (p[beg] == '+' || p[beg] == '-')) {
124
		base = 10;
125
		end = beg + 1;
126
		ntype = fmt[SDEC];
127
		if (!isdigit(p[end]))
128
			goto nonum;
129
	} else {
130
decimal:	base = 10;
131
		end = beg;
132
		ntype = fmt[DEC];
133
		if (!isdigit(p[end])) {
134
nonum:			msgq(sp, M_ERR, "Cursor not in a number");
135
			return (1);
136
		}
137
	}
138
139
	/* Find the end of the word, possibly correcting the base. */
140
	while (++end < len) {
141
		switch (base) {
142
		case 8:
143
			if (isoctal(p[end]))
144
				continue;
145
			if (p[end] == '8' || p[end] == '9') {
146
				base = 10;
147
				ntype = fmt[DEC];
148
				continue;
149
			}
150
			break;
151
		case 10:
152
			if (isdigit(p[end]))
153
				continue;
154
			break;
155
		case 16:
156
			if (ishex(p[end]))
157
				continue;
158
			break;
159
		default:
160
			abort();
161
			/* NOTREACHED */
162
		}
163
		break;
164
	}
165
	wlen = (end - beg);
166
167
	/*
168
	 * XXX
169
	 * If the line was at the end of the buffer, we have to copy it
170
	 * so we can guarantee that it's NULL-terminated.  We make the
171
	 * buffer big enough to fit the line changes as well, and only
172
	 * allocate once.
173
	 */
174
	GET_SPACE_RET(sp, bp, blen, len + 50);
175
	if (end == len) {
176
		memmove(bp, &p[beg], wlen);
177
		bp[wlen] = '\0';
178
		t = bp;
179
	} else
180
		t = &p[beg];
181
182
	/*
183
	 * Octal or hex deal in unsigned longs, everything else is done
184
	 * in signed longs.
185
	 */
186
	if (base == 10) {
187
		if ((nret = nget_slong(&lval, t, NULL, 10)) != NUM_OK)
188
			goto err;
189
		ltmp = vp->character == '-' ? -change : change;
190
		if (lval > 0 && ltmp > 0 && !NPFITS(LONG_MAX, lval, ltmp)) {
191
			nret = NUM_OVER;
192
			goto err;
193
		}
194
		if (lval < 0 && ltmp < 0 && !NNFITS(LONG_MIN, lval, ltmp)) {
195
			nret = NUM_UNDER;
196
			goto err;
197
		}
198
		lval += ltmp;
199
		/* If we cross 0, signed numbers lose their sign. */
200
		if (lval == 0 && ntype == fmt[SDEC])
201
			ntype = fmt[DEC];
202
		nlen = snprintf(nbuf, sizeof(nbuf), ntype, lval);
203
		if (nlen >= sizeof(nbuf))
204
			nlen = sizeof(nbuf) - 1;
205
	} else {
206
		if ((nret = nget_uslong(&ulval, t, NULL, base)) != NUM_OK)
207
			goto err;
208
		if (vp->character == '+') {
209
			if (!NPFITS(ULONG_MAX, ulval, change)) {
210
				nret = NUM_OVER;
211
				goto err;
212
			}
213
			ulval += change;
214
		} else {
215
			if (ulval < change) {
216
				nret = NUM_UNDER;
217
				goto err;
218
			}
219
			ulval -= change;
220
		}
221
222
		/* Correct for literal "0[Xx]" in format. */
223
		if (base == 16)
224
			wlen -= 2;
225
226
		nlen = snprintf(nbuf, sizeof(nbuf), ntype, wlen, ulval);
227
		if (nlen >= sizeof(nbuf))
228
			nlen = sizeof(nbuf) - 1;
229
	}
230
231
	/* Build the new line. */
232
	memmove(bp, p, beg);
233
	memmove(bp + beg, nbuf, nlen);
234
	memmove(bp + beg + nlen, p + end, len - beg - (end - beg));
235
	len = beg + nlen + (len - beg - (end - beg));
236
237
	nret = NUM_OK;
238
	rval = db_set(sp, vp->m_start.lno, bp, len);
239
240
	if (0) {
241
err:		rval = 1;
242
		inc_err(sp, nret);
243
	}
244
	if (bp != NULL)
245
		FREE_SPACE(sp, bp, blen);
246
	return (rval);
247
}
248
249
static void
250
inc_err(SCR *sp, enum nresult nret)
251
{
252
	switch (nret) {
253
	case NUM_ERR:
254
		break;
255
	case NUM_OK:
256
		abort();
257
		/* NOREACHED */
258
	case NUM_OVER:
259
		msgq(sp, M_ERR, "Resulting number too large");
260
		break;
261
	case NUM_UNDER:
262
		msgq(sp, M_ERR, "Resulting number too small");
263
		break;
264
	}
265
}