source: trunk/third/nvi/vi/v_increment.c @ 14302

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