/*- * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 1992, 1993, 1994, 1995, 1996 * Keith Bostic. All rights reserved. * * See the LICENSE file for redistribution information. */ #include "config.h" #ifndef lint static const char sccsid[] = "@(#)v_ulcase.c 10.7 (Berkeley) 3/6/96"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include "../common/common.h" #include "vi.h" static int ulcase __P((SCR *, recno_t, CHAR_T *, size_t, size_t, size_t)); /* * v_ulcase -- [count]~ * Toggle upper & lower case letters. * * !!! * Historic vi didn't permit ~ to cross newline boundaries. I can * think of no reason why it shouldn't, which at least lets the user * auto-repeat through a paragraph. * * !!! * In historic vi, the count was ignored. It would have been better * if there had been an associated motion, but it's too late to make * that the default now. * * PUBLIC: int v_ulcase __P((SCR *, VICMD *)); */ int v_ulcase(sp, vp) SCR *sp; VICMD *vp; { recno_t lno; size_t cno, lcnt, len; u_long cnt; char *p; lno = vp->m_start.lno; cno = vp->m_start.cno; for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt > 0; cno = 0) { /* SOF is an error, EOF is an infinite count sink. */ if (db_get(sp, lno, 0, &p, &len)) { if (lno == 1) { v_emsg(sp, NULL, VIM_EMPTY); return (1); } --lno; break; } /* Empty lines decrement the count by one. */ if (len == 0) { --cnt; vp->m_final.cno = 0; continue; } if (cno + cnt >= len) { lcnt = len - 1; cnt -= len - cno; vp->m_final.cno = len - 1; } else { lcnt = cno + cnt - 1; cnt = 0; vp->m_final.cno = lcnt + 1; } if (ulcase(sp, lno, p, len, cno, lcnt)) return (1); if (cnt > 0) ++lno; } vp->m_final.lno = lno; return (0); } /* * v_mulcase -- [count]~[count]motion * Toggle upper & lower case letters over a range. * * PUBLIC: int v_mulcase __P((SCR *, VICMD *)); */ int v_mulcase(sp, vp) SCR *sp; VICMD *vp; { CHAR_T *p; size_t len; recno_t lno; for (lno = vp->m_start.lno;;) { if (db_get(sp, lno, DBG_FATAL, &p, &len)) return (1); if (len != 0 && ulcase(sp, lno, p, len, lno == vp->m_start.lno ? vp->m_start.cno : 0, !F_ISSET(vp, VM_LMODE) && lno == vp->m_stop.lno ? vp->m_stop.cno : len)) return (1); if (++lno > vp->m_stop.lno) break; } /* * XXX * I didn't create a new motion command when I added motion semantics * for ~. While that's the correct way to do it, that choice would * have required changes all over the vi directory for little gain. * Instead, we pretend it's a yank command. Note, this means that we * follow the cursor motion rules for yank commands, but that seems * reasonable to me. */ return (0); } /* * ulcase -- * Change part of a line's case. */ static int ulcase(sp, lno, lp, len, scno, ecno) SCR *sp; recno_t lno; CHAR_T *lp; size_t len, scno, ecno; { size_t blen; int change, rval; CHAR_T ch, *p, *t; char *bp; GET_SPACE_RET(sp, bp, blen, len); memmove(bp, lp, len); change = rval = 0; for (p = bp + scno, t = bp + ecno + 1; p < t; ++p) { ch = *(u_char *)p; if (islower(ch)) { *p = toupper(ch); change = 1; } else if (isupper(ch)) { *p = tolower(ch); change = 1; } } if (change && db_set(sp, lno, bp, len)) rval = 1; FREE_SPACE(sp, bp, blen); return (rval); }