home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / v_increment.c < prev    next >
C/C++ Source or Header  |  1996-04-27  |  6KB  |  268 lines

  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
  13. static 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.  
  31. static 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.  
  44. static 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.  */
  52. int
  53. v_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 {
  134. decimal:    base = 10;
  135.         end = beg;
  136.         ntype = fmt[DEC];
  137.         if (!isdigit(p[end])) {
  138. nonum:            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) {
  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(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. }
  268.