home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / ex / ex_global.c < prev    next >
C/C++ Source or Header  |  1996-10-10  |  8KB  |  329 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[] = "@(#)ex_global.c    10.22 (Berkeley) 10/10/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18.  
  19. #include <bitstring.h>
  20. #include <ctype.h>
  21. #include <errno.h>
  22. #include <limits.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <unistd.h>
  27.  
  28. #include "../common/common.h"
  29.  
  30. enum which {GLOBAL, V};
  31.  
  32. static int ex_g_setup __P((SCR *, EXCMD *, enum which));
  33.  
  34. /*
  35.  * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
  36.  *    Exec on lines matching a pattern.
  37.  *
  38.  * PUBLIC: int ex_global __P((SCR *, EXCMD *));
  39.  */
  40. int
  41. ex_global(sp, cmdp)
  42.     SCR *sp;
  43.     EXCMD *cmdp;
  44. {
  45.     return (ex_g_setup(sp,
  46.         cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL));
  47. }
  48.  
  49. /*
  50.  * ex_v -- [line [,line]] v /pattern/ [commands]
  51.  *    Exec on lines not matching a pattern.
  52.  *
  53.  * PUBLIC: int ex_v __P((SCR *, EXCMD *));
  54.  */
  55. int
  56. ex_v(sp, cmdp)
  57.     SCR *sp;
  58.     EXCMD *cmdp;
  59. {
  60.     return (ex_g_setup(sp, cmdp, V));
  61. }
  62.  
  63. /*
  64.  * ex_g_setup --
  65.  *    Ex global and v commands.
  66.  */
  67. static int
  68. ex_g_setup(sp, cmdp, cmd)
  69.     SCR *sp;
  70.     EXCMD *cmdp;
  71.     enum which cmd;
  72. {
  73.     CHAR_T *ptrn, *p, *t;
  74.     EXCMD *ecp;
  75.     MARK abs;
  76.     RANGE *rp;
  77.     busy_t btype;
  78.     recno_t start, end;
  79.     regex_t *re;
  80.     regmatch_t match[1];
  81.     size_t len;
  82.     int cnt, delim, eval;
  83.     char *dbp;
  84.  
  85.     NEEDFILE(sp, cmdp);
  86.  
  87.     if (F_ISSET(sp, SC_EX_GLOBAL)) {
  88.         msgq(sp, M_ERR,
  89.     "124|The %s command can't be used as part of a global or v command",
  90.             cmdp->cmd->name);
  91.         return (1);
  92.     }
  93.  
  94.     /*
  95.      * Skip leading white space.  Historic vi allowed any non-alphanumeric
  96.      * to serve as the global command delimiter.
  97.      */
  98.     if (cmdp->argc == 0)
  99.         goto usage;
  100.     for (p = cmdp->argv[0]->bp; isblank(*p); ++p);
  101.     if (*p == '\0' || isalnum(*p) ||
  102.         *p == '\\' || *p == '|' || *p == '\n') {
  103. usage:        ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
  104.         return (1);
  105.     }
  106.     delim = *p++;
  107.  
  108.     /*
  109.      * Get the pattern string, toss escaped characters.
  110.      *
  111.      * QUOTING NOTE:
  112.      * Only toss an escaped character if it escapes a delimiter.
  113.      */
  114.     for (ptrn = t = p;;) {
  115.         if (p[0] == '\0' || p[0] == delim) {
  116.             if (p[0] == delim)
  117.                 ++p;
  118.             /*
  119.              * !!!
  120.              * Nul terminate the pattern string -- it's passed
  121.              * to regcomp which doesn't understand anything else.
  122.              */
  123.             *t = '\0';
  124.             break;
  125.         }
  126.         if (p[0] == '\\')
  127.             if (p[1] == delim)
  128.                 ++p;
  129.             else if (p[1] == '\\')
  130.                 *t++ = *p++;
  131.         *t++ = *p++;
  132.     }
  133.  
  134.     /* If the pattern string is empty, use the last one. */
  135.     if (*ptrn == '\0') {
  136.         if (sp->re == NULL) {
  137.             ex_emsg(sp, NULL, EXM_NOPREVRE);
  138.             return (1);
  139.         }
  140.  
  141.         /* Re-compile the RE if necessary. */
  142.         if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
  143.             sp->re, sp->re_len, NULL, NULL, &sp->re_c, RE_C_SEARCH))
  144.             return (1);
  145.     } else {
  146.         /* Compile the RE. */
  147.         if (re_compile(sp, ptrn, t - ptrn,
  148.             &sp->re, &sp->re_len, &sp->re_c, RE_C_SEARCH))
  149.             return (1);
  150.  
  151.         /*
  152.          * Set saved RE.  Historic practice is that globals set
  153.          * direction as well as the RE.
  154.          */
  155.         sp->searchdir = FORWARD;
  156.     }
  157.     re = &sp->re_c;
  158.  
  159.     /* The global commands always set the previous context mark. */
  160.     abs.lno = sp->lno;
  161.     abs.cno = sp->cno;
  162.     if (mark_set(sp, ABSMARK1, &abs, 1))
  163.         return (1);
  164.  
  165.     /* Get an EXCMD structure. */
  166.     CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
  167.     CIRCLEQ_INIT(&ecp->rq);
  168.  
  169.     /*
  170.      * Get a copy of the command string; the default command is print.
  171.      * Don't worry about a set of <blank>s with no command, that will
  172.      * default to print in the ex parser.  We need to have two copies
  173.      * because the ex parser may step on the command string when it's
  174.      * parsing it.
  175.      */
  176.     if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) {
  177.         p = "pp";
  178.         len = 1;
  179.     }
  180.  
  181.     MALLOC_RET(sp, ecp->cp, char *, len * 2);
  182.     ecp->o_cp = ecp->cp;
  183.     ecp->o_clen = len;
  184.     memcpy(ecp->cp + len, p, len);
  185.     ecp->range_lno = OOBLNO;
  186.     FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V);
  187.     LIST_INSERT_HEAD(&sp->gp->ecq, ecp, q);
  188.  
  189.     /*
  190.      * For each line...  The semantics of global matching are that we first
  191.      * have to decide which lines are going to get passed to the command,
  192.      * and then pass them to the command, ignoring other changes.  There's
  193.      * really no way to do this in a single pass, since arbitrary line
  194.      * creation, deletion and movement can be done in the ex command.  For
  195.      * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
  196.      * What we do is create linked list of lines that are tracked through
  197.      * each ex command.  There's a callback routine which the DB interface
  198.      * routines call when a line is created or deleted.  This doesn't help
  199.      * the layering much.
  200.      */
  201.     btype = BUSY_ON;
  202.     cnt = INTERRUPT_CHECK;
  203.     for (start = cmdp->addr1.lno,
  204.         end = cmdp->addr2.lno; start <= end; ++start) {
  205.         if (cnt-- == 0) {
  206.             if (INTERRUPTED(sp)) {
  207.                 LIST_REMOVE(ecp, q);
  208.                 free(ecp->cp);
  209.                 free(ecp);
  210.                 break;
  211.             }
  212.             search_busy(sp, btype);
  213.             btype = BUSY_UPDATE;
  214.             cnt = INTERRUPT_CHECK;
  215.         }
  216.         if (db_get(sp, start, DBG_FATAL, &dbp, &len))
  217.             return (1);
  218.         match[0].rm_so = 0;
  219.         match[0].rm_eo = len;
  220.         switch (eval =
  221.             regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) {
  222.         case 0:
  223.             if (cmd == V)
  224.                 continue;
  225.             break;
  226.         case REG_NOMATCH:
  227.             if (cmd == GLOBAL)
  228.                 continue;
  229.             break;
  230.         default:
  231.             re_error(sp, eval, &sp->re_c);
  232.             break;
  233.         }
  234.  
  235.         /* If follows the last entry, extend the last entry's range. */
  236.         if ((rp = ecp->rq.cqh_last) != (void *)&ecp->rq &&
  237.             rp->stop == start - 1) {
  238.             ++rp->stop;
  239.             continue;
  240.         }
  241.  
  242.         /* Allocate a new range, and append it to the list. */
  243.         CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
  244.         if (rp == NULL)
  245.             return (1);
  246.         rp->start = rp->stop = start;
  247.         CIRCLEQ_INSERT_TAIL(&ecp->rq, rp, q);
  248.     }
  249.     search_busy(sp, BUSY_OFF);
  250.     return (0);
  251. }
  252.  
  253. /*
  254.  * ex_g_insdel --
  255.  *    Update the ranges based on an insertion or deletion.
  256.  *
  257.  * PUBLIC: int ex_g_insdel __P((SCR *, lnop_t, recno_t));
  258.  */
  259. int
  260. ex_g_insdel(sp, op, lno)
  261.     SCR *sp;
  262.     lnop_t op;
  263.     recno_t lno;
  264. {
  265.     EXCMD *ecp;
  266.     RANGE *nrp, *rp;
  267.  
  268.     /* All insert/append operations are done as inserts. */
  269.     if (op == LINE_APPEND)
  270.         abort();
  271.  
  272.     if (op == LINE_RESET)
  273.         return (0);
  274.  
  275.     for (ecp = sp->gp->ecq.lh_first; ecp != NULL; ecp = ecp->q.le_next) {
  276.         if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V))
  277.             continue;
  278.         for (rp = ecp->rq.cqh_first; rp != (void *)&ecp->rq; rp = nrp) {
  279.             nrp = rp->q.cqe_next;
  280.  
  281.             /* If range less than the line, ignore it. */
  282.             if (rp->stop < lno)
  283.                 continue;
  284.             
  285.             /*
  286.              * If range greater than the line, decrement or
  287.              * increment the range.
  288.              */
  289.             if (rp->start > lno) {
  290.                 if (op == LINE_DELETE) {
  291.                     --rp->start;
  292.                     --rp->stop;
  293.                 } else {
  294.                     ++rp->start;
  295.                     ++rp->stop;
  296.                 }
  297.                 continue;
  298.             }
  299.  
  300.             /*
  301.              * Lno is inside the range, decrement the end point
  302.              * for deletion, and split the range for insertion.
  303.              * In the latter case, since we're inserting a new
  304.              * element, neither range can be exhausted.
  305.              */
  306.             if (op == LINE_DELETE) {
  307.                 if (rp->start > --rp->stop) {
  308.                     CIRCLEQ_REMOVE(&ecp->rq, rp, q);
  309.                     free(rp);
  310.                 }
  311.             } else {
  312.                 CALLOC_RET(sp, nrp, RANGE *, 1, sizeof(RANGE));
  313.                 nrp->start = lno + 1;
  314.                 nrp->stop = rp->stop + 1;
  315.                 rp->stop = lno - 1;
  316.                 CIRCLEQ_INSERT_AFTER(&ecp->rq, rp, nrp, q);
  317.                 rp = nrp;
  318.             }
  319.         }
  320.  
  321.         /*
  322.          * If the command deleted/inserted lines, the cursor moves to
  323.          * the line after the deleted/inserted line.
  324.          */
  325.         ecp->range_lno = lno;
  326.     }
  327.     return (0);
  328. }
  329.