Logo Search packages:      
Sourcecode: vile version File versions

oneliner.c

/*
 *    A few functions that used to operate on single whole lines, mostly
 *    here to support the globals() function.  They now work on regions.
 *    Copyright (c) 1990, 1995-1999 by Paul Fox, except for delins(), which is
 *    Copyright (c) 1986 by University of Toronto, as noted below.
 *
 * $Header: /usr/build/vile/vile/RCS/oneliner.c,v 1.102 2003/03/18 02:20:27 tom Exp $
 */

#include    "estruct.h"
#include    "edef.h"

#define PLIST     0x01
#define PNUMS     0x02

static int delins(regexp * exp, char *sourc, int lensrc);
static int substline(regexp * exp, int nth_occur, int printit, int globally, int *confirmp);
static int substreg1(int needpats, int use_opts, int is_globalsub);

static int lines_changed, total_changes;

/*
 * put lines in a popup window
 */
static int
pregion(UINT flag)
{
    register WINDOW *wp;
    register BUFFER *bp;
    register int status;
    REGION region;
    register LINEPTR linep;
    BUFFER *oldbp = curbp;

    if ((status = get_fl_region(&region)) != TRUE) {
      return (status);
    }

    linep = region.r_orig.l;  /* Current line.        */

    /* first check if we are already here */
    bp = bfind(P_LINES_BufName, 0);
    if (bp == NULL) {
      return FALSE;
    }

    if (bp == curbp) {
      mlforce("[Can't do that from this buffer.]");
      return FALSE;
    }

    if (!calledbefore) {      /* fresh start */
      /* bring p-lines up */
      if (popupbuff(bp) != TRUE
          || bclear(bp) != TRUE) {
          return FALSE;
      }

      calledbefore = TRUE;
    }

    do {
      if (!addline(bp, linep->l_text, llength(linep)))
          break;        /* out of memory */
      if (flag & PNUMS) {
          BUFFER *savebp = curbp;
          WINDOW *savewp = curwp;
          curbp = bp;
          curwp = bp2any_wp(bp);
          DOT.l = lback(buf_head(bp));
          DOT.o = 0;
          bprintf("%s:%d:", oldbp->b_bname, line_no(oldbp, linep));
          DOT.o = 0;
          curbp = savebp;
          curwp = savewp;
      }
      linep = lforw(linep);
    } while (linep != region.r_end.l);

    set_bname(bp, P_LINES_BufName);
    set_rdonly(bp, non_filename(), MDVIEW);
    set_b_val(bp, VAL_TAB, tabstop_val(curbp));

    for_each_visible_window(wp) {
      if (wp->w_bufp == bp) {
          make_local_w_val(wp, WMDLIST);
          set_w_val(wp, WMDLIST, ((flag & PLIST) != 0));
          wp->w_flag |= WFMODE | WFFORCE;
      }
    }
    return TRUE;
}

int
llineregion(void)
{
    return pregion(PLIST);
}

int
pplineregion(void)
{
    return pregion(PNUMS);
}

int
plineregion(void)
{
    return pregion(0);
}

static regexp *substexp;

int
substregion(void)
{
    return substreg1(TRUE, TRUE, FALSE);
}

int
subst_again_region(void)
{
    return substreg1(FALSE, TRUE, FALSE);
}

int
subst_all_region(void)
{
    return substreg1(TRUE, TRUE, TRUE);
}

/* traditional vi & command */
/* ARGSUSED */
int
subst_again(int f GCC_UNUSED, int n GCC_UNUSED)
{
    int s;
    MARK curpos;

    curpos = DOT;

    /* the region spans just the line */
    MK.l = DOT.l;
    DOT.o = 0;
    MK.o = llength(MK.l);
    s = substreg1(FALSE, FALSE, FALSE);
    if (s != TRUE) {
      mlforce("[No match.]");
      DOT = curpos;
      return s;
    }
    swapmark();
    return TRUE;
}

static int
substreg1(int needpats, int use_opts, int is_globalsub)
{
    int c, status;
    static int printit, globally, nth_occur, confirm;
    REGION region;
    LINEPTR oline;
    int getopts = FALSE;
    TBUFF *newpattern;

    if ((status = get_fl_region(&region)) != TRUE) {
      return (status);
    }

    if (calledbefore == FALSE && needpats) {
      c = kbd_delimiter();
      if ((status = readpattern("substitute pattern: ", &searchpat,
                          &gregexp, c, FALSE)) != TRUE) {
          if (status != ABORT)
            mlforce("[No pattern.]");
          return status;
      }

      if (gregexp) {
          FreeIfNeeded(substexp);
          substexp = castalloc(regexp, (size_t) (gregexp->size));
          (void) memcpy((char *) substexp, (char *) gregexp,
                    (size_t) gregexp->size);
      }

      newpattern = 0;
      if (tb_length(replacepat) != 0)
          tb_copy(&newpattern, replacepat);
      status = readpattern("replacement string: ",
                       &newpattern, (regexp **) 0, c, FALSE);
      if (status == ABORT) {
          /* if false, the pattern is null, which is okay... */
          tb_free(&newpattern);
          return status;
      }
      tb_free(&replacepat);
      replacepat = newpattern;

      nth_occur = -1;
      confirm = printit = globally = FALSE;
      getopts = (lastkey == c);     /* the user may have
                                 something to add */

    } else {
      if (!use_opts) {
          nth_occur = -1;
          printit = globally = FALSE;
      } else {
          if (more_named_cmd()) {
            unkeystroke(lastkey);
            nth_occur = -1;
            printit = globally = FALSE;
            getopts = TRUE;
          }
      }
    }

    if (getopts) {
      char buf[4];
      char *bp = buf;

      buf[0] = EOS;
      status = mlreply(
                      "(g)lobally, ([1-9])th occurrence on line, (c)onfirm, and/or (p)rint result: ",
                      buf, sizeof buf);
      if (status == ABORT) {
          return FALSE;
      }
      nth_occur = 0;
      while (*bp) {
          if (*bp == 'p' && !printit) {
            printit = TRUE;
          } else if (*bp == 'g' &&
                   !globally && !nth_occur) {
            globally = TRUE;
          } else if (isDigit(*bp) &&
                   !nth_occur && !globally) {
            nth_occur = *bp - '0';
            globally = TRUE;
          } else if (*bp == 'c' && !confirm) {
            confirm = TRUE;
          } else if (!isSpace(*bp)) {
            mlforce("[Unknown action %s]", buf);
            return FALSE;
          }
          bp++;
      }
      if (!nth_occur)
          nth_occur = -1;
    }

    if (is_globalsub)
      globally = TRUE;

    lines_changed =
      total_changes = 0;
    DOT.l = region.r_orig.l;  /* Current line.        */
    do {
      oline = DOT.l;
      if ((status = substline(substexp, nth_occur, printit,
                        globally, &confirm)) != TRUE) {
          return status;
      }
      DOT.l = lforw(oline);
    } while (!sameline(DOT, region.r_end));
    calledbefore = TRUE;

    if (do_report(total_changes)) {
      mlforce("[%d change%s on %d line%s]",
            total_changes, PLURAL(total_changes),
            lines_changed, PLURAL(lines_changed));
    }
    return TRUE;
}

/* show the pattern we've matched (at least one space, even for empty-string) */
static void
showpat(regexp * rp, int on)
{
    register LINEPTR lp;
    int row;

    for (lp = curwp->w_line.l, row = curwp->w_toprow;
       lp != DOT.l;
       lp = lforw(lp))
      row += line_height(curwp, lp);

    hilite(row,
         offs2col(curwp, lp, DOT.o),
         offs2col(curwp, lp, (C_NUM) (DOT.o + rp->mlen)) + (rp->mlen == 0),
         on);
}

static int
substline(regexp * exp, int nth_occur, int printit, int globally, int *confirmp)
{
    int foundit;
    int again = 0;
    register int s;
    register int which_occur = 0;
    int matched_at_eol = FALSE;
    int yes, c, skipped;

    TRACE((T_CALLED
         "substline(exp=%p, nth_occur=%d, printit=%d, globally=%d, confirmp=%p)\n",
         exp, nth_occur, printit, globally, confirmp));

    /* if the "magic number" hasn't been set yet... */
    if (!exp || UCHAR_AT(exp->program) != REGEXP_MAGIC) {
      mlforce("[No pattern set yet]");
      returnCode(FALSE);
    }

    ignorecase = window_b_val(curwp, MDIGNCASE);

    foundit = FALSE;
    scanboundpos.l = DOT.l;
    scanbound_is_header = FALSE;
    DOT.o = 0;
    do {
      scanboundpos.o = llength(DOT.l);
      s = scanner(exp, FORWARD, FALSE, (int *) 0);
      if (s != TRUE)
          break;

      /* found the pattern */
      foundit = TRUE;
      which_occur++;
      if (nth_occur == -1 || which_occur == nth_occur) {
          (void) setmark();
          /* only allow one match at the end of line, to
             prevent loop with s/$/x/g  */
          if (MK.o == llength(DOT.l)) {
            if (matched_at_eol)
                break;
            matched_at_eol = TRUE;
          }

          /* if we need confirmation, get it */
          skipped = FALSE;
          if (*confirmp) {

            /* force the pattern onto the screen */
            (void) gomark(FALSE, 0);
            if (update(TRUE) == TRUE)
                showpat(exp, TRUE);
            s = mlquickask("Make change [y/n/q/a]?",
                         "ynqa\r", &c);

            showpat(exp, FALSE);

            if (s != TRUE)
                c = 'q';

            switch (c) {
            case 'y':
                yes = TRUE;
                break;
            default:
            case 'n':
                yes = FALSE;
                (void) update(TRUE);
                skipped = TRUE;
                /* so we don't match this again */
                DOT.o += exp->mlen;
                break;
            case 'q':
                mlerase();
                returnCode(FALSE);
            case 'a':
                yes = TRUE;
                *confirmp = FALSE;
                mlerase();
                break;
            }
          } else {
            yes = TRUE;
          }
          if (yes) {
            s = delins(exp, tb_values(replacepat), tb_length(replacepat));
            if (s != TRUE)
                returnCode(s);
            if (!again++)
                lines_changed++;
            total_changes++;
          }
          if (*confirmp && !skipped)      /* force a screen update */
            (void) update(TRUE);

          if (exp->mlen == 0 && forwchar(TRUE, 1) == FALSE)
            break;
          if (nth_occur > 0)
            break;
      } else {          /* non-overlapping matches */
          s = forwchar(TRUE, (int) (exp->mlen));
          if (s != TRUE)
            returnCode(s);
      }
    } while (globally && sameline(scanboundpos, DOT));
    if (foundit && printit) {
      register WINDOW *wp = curwp;
      (void) setmark();
      s = plineregion();
      if (s != TRUE)
          returnCode(s);
      /* back to our buffer */
      swbuffer(wp->w_bufp);
    }
    if (*confirmp)
      mlerase();
    returnCode(TRUE);
}

/*
 * delins, modified by pgf from regsub
 *
 *    Copyright (c) 1986 by University of Toronto.
 *    Written by Henry Spencer.  Not derived from licensed software.
 *
 *    Permission is granted to anyone to use this software for any
 *    purpose on any computer system, and to redistribute it freely,
 *    subject to the following restrictions:
 *
 *    1. The author is not responsible for the consequences of use of
 *          this software, no matter how awful, even if they arise
 *          from defects in it.
 *
 *    2. The origin of this software must not be misrepresented, either
 *          by explicit claim or by omission.
 *
 *    3. Altered versions must be plainly marked as such, and must not
 *          be misrepresented as being the original software.
 */

static char *buf_delins;
static UINT len_delins;

/*
 - delins - perform substitutions after a regexp match
 */
static int
delins(regexp * exp, char *sourc, int lensrc)
{
    size_t dlength;
    int c;
    int no;
    int j;
    int s;
#define NO_CASE   0
#define UPPER_CASE 1
#define LOWER_CASE 2
    int case_next, case_all;

    if (exp == NULL || sourc == NULL) {
      mlforce("BUG: NULL parm to delins");
      return FALSE;
    }
    if (UCHAR_AT(exp->program) != REGEXP_MAGIC) {
      regerror("damaged regexp fed to delins");
      return FALSE;
    }

    dlength = exp->mlen;

    if (buf_delins == NULL || dlength + 1 > len_delins) {
      if (buf_delins)
          free(buf_delins);
      if ((buf_delins = castalloc(char, dlength + 1)) == NULL) {
          mlforce("[Out of memory in delins]");
          return FALSE;
      }
      len_delins = dlength + 1;
    }

    (void) memcpy(buf_delins, exp->startp[0], (size_t) dlength);
    buf_delins[dlength] = EOS;

    if (ldelete((long) dlength, FALSE) != TRUE) {
      mlforce("[Error while deleting]");
      return FALSE;
    }
    case_next = case_all = NO_CASE;
    for (j = 0; j < lensrc; ++j) {
      c = sourc[j];
      no = 0;
      s = TRUE;
      switch (c) {
      case BACKSLASH:
          if (j + 1 >= lensrc)
            return TRUE;
          c = sourc[++j];
          if (!isDigit(c)) {
            /* here's where the \U \E \u \l \t etc.
               special escapes should be implemented */
            switch (c) {
            case 'U':
                case_all = UPPER_CASE;
                break;
            case 'L':
                case_all = LOWER_CASE;
                break;
            case 'u':
                case_next = UPPER_CASE;
                break;
            case 'l':
                case_next = LOWER_CASE;
                break;
            case 'E':
            case 'e':
                case_all = NO_CASE;
                break;
            case 'b':
                s = linsert(1, '\b');
                break;
            case 'f':
                s = linsert(1, '\f');
                break;
            case 'r':
                s = linsert(1, '\r');
                break;
            case 't':
                s = linsert(1, '\t');
                break;
            case 'n':
                s = lnewline();
                break;
            default:
                s = linsert(1, c);
                break;
            }
            break;
          }
          /* else it's a digit --
             get pattern number, and fall through */
          no = c - '0';
          /* FALLTHROUGH */
      case '&':
          if (exp->startp[no] != NULL && exp->endp[no] != NULL) {
            char *cp = buf_delins;
            long len = exp->endp[no] - exp->startp[no];
            long adj = exp->startp[no] - exp->startp[0];
            cp += (size_t) adj;
            while ((s == TRUE) && len--) {
                c = *cp++;
                if (c == EOS) {
                  mlforce("BUG: mangled replace");
                  return FALSE;
                }
                if (c == '\n')
                  s = lnewline();
                else if (case_next == NO_CASE && case_all == NO_CASE)
                  s = linsert(1, c);
                else {
                  int direction = case_next != NO_CASE ? case_next : case_all;
                  case_next = NO_CASE;
                  /* Somewhat convoluted to handle
                     \u\L correctly (upper case first
                     char, lower case remainder).
                     This is the perl model, not the vi model. */
                  if (isUpper(c) && (direction == LOWER_CASE))
                      c = toLower(c);
                  if (isLower(c) && (direction == UPPER_CASE))
                      c = toUpper(c);
                  s = linsert(1, c);
                }
            }
          }
          break;

      case '\n':
      case '\r':
          s = lnewline();
          break;

      default:
          if (case_next || case_all) {
            int direction = case_next != NO_CASE ? case_next : case_all;
            case_next = NO_CASE;
            /* Somewhat convoluted to handle
               \u\L correctly (upper case first
               char, lower case remainder).
               This is the perl model, not the vi model. */
            if (isUpper(c) && (direction == LOWER_CASE))
                c = toLower(c);
            if (isLower(c) && (direction == UPPER_CASE))
                c = toUpper(c);
          }
          s = linsert(1, c);
          break;
      }
      if (s != TRUE) {
          mlforce("[Out of memory while inserting]");
          return FALSE;
      }
    }
    return TRUE;
}

#if NO_LEAKS
void
onel_leaks(void)
{
    FreeIfNeeded(substexp);
    FreeIfNeeded(buf_delins);
}
#endif

Generated by  Doxygen 1.6.0   Back to index