119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: v_ulcase.c,v 10.12 2011/12/02 19:58:32 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter
2819304Speter#include "../common/common.h"
2919304Speter#include "vi.h"
3019304Speter
3119304Speterstatic int ulcase __P((SCR *, recno_t, CHAR_T *, size_t, size_t, size_t));
3219304Speter
3319304Speter/*
3419304Speter * v_ulcase -- [count]~
3519304Speter *	Toggle upper & lower case letters.
3619304Speter *
3719304Speter * !!!
3819304Speter * Historic vi didn't permit ~ to cross newline boundaries.  I can
3919304Speter * think of no reason why it shouldn't, which at least lets the user
4019304Speter * auto-repeat through a paragraph.
4119304Speter *
4219304Speter * !!!
4319304Speter * In historic vi, the count was ignored.  It would have been better
4419304Speter * if there had been an associated motion, but it's too late to make
4519304Speter * that the default now.
4619304Speter *
4719304Speter * PUBLIC: int v_ulcase __P((SCR *, VICMD *));
4819304Speter */
4919304Speterint
50254225Speterv_ulcase(SCR *sp, VICMD *vp)
5119304Speter{
5219304Speter	recno_t lno;
5319304Speter	size_t cno, lcnt, len;
5419304Speter	u_long cnt;
55254225Speter	CHAR_T *p;
5619304Speter
5719304Speter	lno = vp->m_start.lno;
5819304Speter	cno = vp->m_start.cno;
5919304Speter
6019304Speter	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt > 0; cno = 0) {
6119304Speter		/* SOF is an error, EOF is an infinite count sink. */
6219304Speter		if (db_get(sp, lno, 0, &p, &len)) {
6319304Speter			if (lno == 1) {
6419304Speter				v_emsg(sp, NULL, VIM_EMPTY);
6519304Speter				return (1);
6619304Speter			}
6719304Speter			--lno;
6819304Speter			break;
6919304Speter		}
7019304Speter
7119304Speter		/* Empty lines decrement the count by one. */
7219304Speter		if (len == 0) {
7319304Speter			--cnt;
7419304Speter			vp->m_final.cno = 0;
7519304Speter			continue;
7619304Speter		}
7719304Speter
7819304Speter		if (cno + cnt >= len) {
7919304Speter			lcnt = len - 1;
8019304Speter			cnt -= len - cno;
8119304Speter
8219304Speter			vp->m_final.cno = len - 1;
8319304Speter		} else {
8419304Speter			lcnt = cno + cnt - 1;
8519304Speter			cnt = 0;
8619304Speter
8719304Speter			vp->m_final.cno = lcnt + 1;
8819304Speter		}
8919304Speter
9019304Speter		if (ulcase(sp, lno, p, len, cno, lcnt))
9119304Speter			return (1);
9219304Speter
9319304Speter		if (cnt > 0)
9419304Speter			++lno;
9519304Speter	}
9619304Speter
9719304Speter	vp->m_final.lno = lno;
9819304Speter	return (0);
9919304Speter}
10019304Speter
10119304Speter/*
10219304Speter * v_mulcase -- [count]~[count]motion
10319304Speter *	Toggle upper & lower case letters over a range.
10419304Speter *
10519304Speter * PUBLIC: int v_mulcase __P((SCR *, VICMD *));
10619304Speter */
10719304Speterint
108254225Speterv_mulcase(SCR *sp, VICMD *vp)
10919304Speter{
11019304Speter	CHAR_T *p;
11119304Speter	size_t len;
11219304Speter	recno_t lno;
11319304Speter
11419304Speter	for (lno = vp->m_start.lno;;) {
11519304Speter		if (db_get(sp, lno, DBG_FATAL, &p, &len))
11619304Speter			return (1);
11719304Speter		if (len != 0 && ulcase(sp, lno, p, len,
11819304Speter		    lno == vp->m_start.lno ? vp->m_start.cno : 0,
11919304Speter		    !F_ISSET(vp, VM_LMODE) &&
12019304Speter		    lno == vp->m_stop.lno ? vp->m_stop.cno : len))
12119304Speter			return (1);
12219304Speter
12319304Speter		if (++lno > vp->m_stop.lno)
12419304Speter			break;
12519304Speter	}
12619304Speter
12719304Speter	/*
12819304Speter	 * XXX
12919304Speter	 * I didn't create a new motion command when I added motion semantics
13019304Speter	 * for ~.  While that's the correct way to do it, that choice would
13119304Speter	 * have required changes all over the vi directory for little gain.
13219304Speter	 * Instead, we pretend it's a yank command.  Note, this means that we
13319304Speter	 * follow the cursor motion rules for yank commands, but that seems
13419304Speter	 * reasonable to me.
13519304Speter	 */
13619304Speter	return (0);
13719304Speter}
13819304Speter
13919304Speter/*
14019304Speter * ulcase --
14119304Speter *	Change part of a line's case.
14219304Speter */
14319304Speterstatic int
144254225Speterulcase(SCR *sp, recno_t lno, CHAR_T *lp, size_t len, size_t scno, size_t ecno)
14519304Speter{
14619304Speter	size_t blen;
14719304Speter	int change, rval;
148254225Speter	ARG_CHAR_T ch;
149254225Speter	CHAR_T *p, *t, *bp;
15019304Speter
151254225Speter	GET_SPACE_RETW(sp, bp, blen, len);
152254225Speter	MEMMOVE(bp, lp, len);
15319304Speter
15419304Speter	change = rval = 0;
15519304Speter	for (p = bp + scno, t = bp + ecno + 1; p < t; ++p) {
156254225Speter		ch = (UCHAR_T)*p;
157254225Speter		if (ISLOWER(ch)) {
158254225Speter			*p = TOUPPER(ch);
15919304Speter			change = 1;
160254225Speter		} else if (ISUPPER(ch)) {
161254225Speter			*p = TOLOWER(ch);
16219304Speter			change = 1;
16319304Speter		}
16419304Speter	}
16519304Speter
16619304Speter	if (change && db_set(sp, lno, bp, len))
16719304Speter		rval = 1;
16819304Speter
169254225Speter	FREE_SPACEW(sp, bp, blen);
17019304Speter	return (rval);
17119304Speter}
172