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: ex_join.c,v 10.17 2004/03/16 14:14:04 skimo Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
18254225Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <limits.h>
2319304Speter#include <stdio.h>
2419304Speter#include <stdlib.h>
2519304Speter#include <string.h>
2619304Speter
2719304Speter#include "../common/common.h"
2819304Speter
2919304Speter/*
3019304Speter * ex_join -- :[line [,line]] j[oin][!] [count] [flags]
3119304Speter *	Join lines.
3219304Speter *
3319304Speter * PUBLIC: int ex_join __P((SCR *, EXCMD *));
3419304Speter */
3519304Speterint
36254225Speterex_join(SCR *sp, EXCMD *cmdp)
3719304Speter{
3819304Speter	recno_t from, to;
3919304Speter	size_t blen, clen, len, tlen;
40254225Speter	int echar = 0, extra, first;
41254225Speter	CHAR_T *bp, *tbp = NULL;
42254225Speter	CHAR_T *p;
4319304Speter
4419304Speter	NEEDFILE(sp, cmdp);
4519304Speter
4619304Speter	from = cmdp->addr1.lno;
4719304Speter	to = cmdp->addr2.lno;
4819304Speter
4919304Speter	/* Check for no lines to join. */
5019304Speter	if (!db_exist(sp, from + 1)) {
5119304Speter		msgq(sp, M_ERR, "131|No following lines to join");
5219304Speter		return (1);
5319304Speter	}
5419304Speter
55254225Speter	GET_SPACE_RETW(sp, bp, blen, 256);
5619304Speter
5719304Speter	/*
5819304Speter	 * The count for the join command was off-by-one,
5919304Speter	 * historically, to other counts for other commands.
6019304Speter	 */
61254225Speter	if (F_ISSET(cmdp, E_ADDR_DEF) || cmdp->addrcnt == 1)
6219304Speter		++cmdp->addr2.lno;
6319304Speter
6419304Speter	clen = tlen = 0;
6519304Speter        for (first = 1,
6619304Speter	    from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
6719304Speter		/*
6819304Speter		 * Get next line.  Historic versions of vi allowed "10J" while
6919304Speter		 * less than 10 lines from the end-of-file, so we do too.
7019304Speter		 */
7119304Speter		if (db_get(sp, from, 0, &p, &len)) {
7219304Speter			cmdp->addr2.lno = from - 1;
7319304Speter			break;
7419304Speter		}
7519304Speter
7619304Speter		/* Empty lines just go away. */
7719304Speter		if (len == 0)
7819304Speter			continue;
7919304Speter
8019304Speter		/*
8119304Speter		 * Get more space if necessary.  Note, tlen isn't the length
8219304Speter		 * of the new line, it's roughly the amount of space needed.
8319304Speter		 * tbp - bp is the length of the new line.
8419304Speter		 */
8519304Speter		tlen += len + 2;
86254225Speter		ADD_SPACE_RETW(sp, bp, blen, tlen);
8719304Speter		tbp = bp + clen;
8819304Speter
8919304Speter		/*
9019304Speter		 * Historic practice:
9119304Speter		 *
9219304Speter		 * If force specified, join without modification.
9319304Speter		 * If the current line ends with whitespace, strip leading
9419304Speter		 *    whitespace from the joined line.
9519304Speter		 * If the next line starts with a ), do nothing.
9619304Speter		 * If the current line ends with ., insert two spaces.
9719304Speter		 * Else, insert one space.
9819304Speter		 *
9919304Speter		 * One change -- add ? and ! to the list of characters for
10019304Speter		 * which we insert two spaces.  I expect that POSIX 1003.2
10119304Speter		 * will require this as well.
10219304Speter		 *
10319304Speter		 * Echar is the last character in the last line joined.
10419304Speter		 */
10519304Speter		extra = 0;
10619304Speter		if (!first && !FL_ISSET(cmdp->iflags, E_C_FORCE)) {
10719304Speter			if (isblank(echar))
10819304Speter				for (; len && isblank(*p); --len, ++p);
10919304Speter			else if (p[0] != ')') {
110254225Speter				if (STRCHR(L(".?!"), echar)) {
11119304Speter					*tbp++ = ' ';
11219304Speter					++clen;
11319304Speter					extra = 1;
11419304Speter				}
11519304Speter				*tbp++ = ' ';
11619304Speter				++clen;
11719304Speter				for (; len && isblank(*p); --len, ++p);
11819304Speter			}
11919304Speter		}
12019304Speter
12119304Speter		if (len != 0) {
122254225Speter			MEMCPY(tbp, p, len);
12319304Speter			tbp += len;
12419304Speter			clen += len;
12519304Speter			echar = p[len - 1];
12619304Speter		} else
12719304Speter			echar = ' ';
12819304Speter
12919304Speter		/*
13019304Speter		 * Historic practice for vi was to put the cursor at the first
13119304Speter		 * inserted whitespace character, if there was one, or the
13219304Speter		 * first character of the joined line, if there wasn't, or the
13319304Speter		 * last character of the line if joined to an empty line.  If
13419304Speter		 * a count was specified, the cursor was moved as described
13519304Speter		 * for the first line joined, ignoring subsequent lines.  If
13619304Speter		 * the join was a ':' command, the cursor was placed at the
13719304Speter		 * first non-blank character of the line unless the cursor was
13819304Speter		 * "attracted" to the end of line when the command was executed
13919304Speter		 * in which case it moved to the new end of line.  There are
14019304Speter		 * probably several more special cases, but frankly, my dear,
14119304Speter		 * I don't give a damn.  This implementation puts the cursor
14219304Speter		 * on the first inserted whitespace character, the first
14319304Speter		 * character of the joined line, or the last character of the
14419304Speter		 * line regardless.  Note, if the cursor isn't on the joined
14519304Speter		 * line (possible with : commands), it is reset to the starting
14619304Speter		 * line.
14719304Speter		 */
14819304Speter		if (first) {
14919304Speter			sp->cno = (tbp - bp) - (1 + extra);
15019304Speter			first = 0;
15119304Speter		} else
15219304Speter			sp->cno = (tbp - bp) - len - (1 + extra);
15319304Speter	}
15419304Speter	sp->lno = cmdp->addr1.lno;
15519304Speter
15619304Speter	/* Delete the joined lines. */
15719304Speter        for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to)
15819304Speter		if (db_delete(sp, to))
15919304Speter			goto err;
16019304Speter
16119304Speter	/* If the original line changed, reset it. */
16219304Speter	if (!first && db_set(sp, from, bp, tbp - bp)) {
163254225Spetererr:		FREE_SPACEW(sp, bp, blen);
16419304Speter		return (1);
16519304Speter	}
166254225Speter	FREE_SPACEW(sp, bp, blen);
16719304Speter
16819304Speter	sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1;
16919304Speter	return (0);
17019304Speter}
171